410
BIN
assets/images/logo/androidlogo.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 43 KiB |
BIN
assets/images/wait_loading.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
@ -1,4 +1,8 @@
|
||||
# Uncomment this line to define a global platform for your project
|
||||
# source 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git'
|
||||
# source 'https://mirrors.tuna.tsinghua.edu.cn/cocoapods/simple/'
|
||||
|
||||
|
||||
platform :ios, '12.0'
|
||||
# 允许拉取http资源
|
||||
# ENV['COCOAPODS_ALLOW_INSECURE_SOURCES'] = 'true'
|
||||
@ -33,6 +37,9 @@ target 'Runner' do
|
||||
use_frameworks!
|
||||
|
||||
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||
# 集成微信官方 SDK
|
||||
# pod 'WechatOpenSDK-XCFramework', '~> 2.0.5'
|
||||
|
||||
target 'RunnerTests' do
|
||||
inherit! :search_paths
|
||||
end
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
PODS:
|
||||
- app_links (6.4.1):
|
||||
- Flutter
|
||||
- audioplayers_darwin (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
@ -19,7 +21,7 @@ PODS:
|
||||
- fluwx/pay (= 0.0.1)
|
||||
- fluwx/pay (0.0.1):
|
||||
- Flutter
|
||||
- WechatOpenSDK-XCFramework (~> 2.0.4)
|
||||
- WechatOpenSDK-XCFramework (~> 2.0.5)
|
||||
- geolocator_apple (1.2.0):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
@ -33,17 +35,17 @@ PODS:
|
||||
- Flutter
|
||||
- install_plugin (2.0.0):
|
||||
- Flutter
|
||||
- libwebp (1.3.2):
|
||||
- libwebp/demux (= 1.3.2)
|
||||
- libwebp/mux (= 1.3.2)
|
||||
- libwebp/sharpyuv (= 1.3.2)
|
||||
- libwebp/webp (= 1.3.2)
|
||||
- libwebp/demux (1.3.2):
|
||||
- libwebp (1.5.0):
|
||||
- libwebp/demux (= 1.5.0)
|
||||
- libwebp/mux (= 1.5.0)
|
||||
- libwebp/sharpyuv (= 1.5.0)
|
||||
- libwebp/webp (= 1.5.0)
|
||||
- libwebp/demux (1.5.0):
|
||||
- libwebp/webp
|
||||
- libwebp/mux (1.3.2):
|
||||
- libwebp/mux (1.5.0):
|
||||
- libwebp/demux
|
||||
- libwebp/sharpyuv (1.3.2)
|
||||
- libwebp/webp (1.3.2):
|
||||
- libwebp/sharpyuv (1.5.0)
|
||||
- libwebp/webp (1.5.0):
|
||||
- libwebp/sharpyuv
|
||||
- Mantle (2.2.0):
|
||||
- Mantle/extobjc (= 2.2.0)
|
||||
@ -67,9 +69,9 @@ PODS:
|
||||
- FlutterMacOS
|
||||
- record_ios (1.0.0):
|
||||
- Flutter
|
||||
- SDWebImage (5.20.0):
|
||||
- SDWebImage/Core (= 5.20.0)
|
||||
- SDWebImage/Core (5.20.0)
|
||||
- SDWebImage (5.21.2):
|
||||
- SDWebImage/Core (= 5.21.2)
|
||||
- SDWebImage/Core (5.21.2)
|
||||
- SDWebImageWebPCoder (0.14.6):
|
||||
- libwebp (~> 1.0)
|
||||
- SDWebImage/Core (~> 5.17)
|
||||
@ -87,7 +89,7 @@ PODS:
|
||||
- TIMPush (8.6.7019):
|
||||
- TXIMSDK_Plus_iOS_XCFramework (>= 8.6.7019)
|
||||
- TOCropViewController (2.7.4)
|
||||
- TXIMSDK_Plus_iOS_XCFramework (8.6.7019)
|
||||
- TXIMSDK_Plus_iOS_XCFramework (8.6.7040)
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
- video_player_avfoundation (0.0.1):
|
||||
@ -100,9 +102,13 @@ PODS:
|
||||
- Flutter
|
||||
- wakelock_plus (0.0.1):
|
||||
- Flutter
|
||||
- WechatOpenSDK-XCFramework (2.0.4)
|
||||
- webview_flutter_wkwebview (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- WechatOpenSDK-XCFramework (2.0.5)
|
||||
|
||||
DEPENDENCIES:
|
||||
- app_links (from `.symlinks/plugins/app_links/ios`)
|
||||
- audioplayers_darwin (from `.symlinks/plugins/audioplayers_darwin/darwin`)
|
||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
@ -131,6 +137,7 @@ DEPENDENCIES:
|
||||
- video_thumbnail (from `.symlinks/plugins/video_thumbnail/ios`)
|
||||
- volume_controller (from `.symlinks/plugins/volume_controller/ios`)
|
||||
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
|
||||
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
@ -145,6 +152,8 @@ SPEC REPOS:
|
||||
- WechatOpenSDK-XCFramework
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
app_links:
|
||||
:path: ".symlinks/plugins/app_links/ios"
|
||||
audioplayers_darwin:
|
||||
:path: ".symlinks/plugins/audioplayers_darwin/darwin"
|
||||
device_info_plus:
|
||||
@ -201,22 +210,25 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/volume_controller/ios"
|
||||
wakelock_plus:
|
||||
:path: ".symlinks/plugins/wakelock_plus/ios"
|
||||
webview_flutter_wkwebview:
|
||||
:path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
app_links: 3dbc685f76b1693c66a6d9dd1e9ab6f73d97dc0a
|
||||
audioplayers_darwin: 4f9ca89d92d3d21cec7ec580e78ca888e5fb68bd
|
||||
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1
|
||||
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
|
||||
flutter_upgrader: 16a975eb987fc210cdf6bebffe0069a480f80523
|
||||
fluwx: 6bf9c5a3a99ad31b0de137dd92370a0d10a60f4b
|
||||
fluwx: 2ef787502fccb3f3596b380509001a8ea71cbbff
|
||||
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
|
||||
HydraAsync: 8d589bd725b0224f899afafc9a396327405f8063
|
||||
image_cropper: c4326ea50132b1e1564499e5d32a84f01fb03537
|
||||
image_gallery_saver_plus: e597bf65a7846979417a3eae0763b71b6dfec6c3
|
||||
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
|
||||
install_plugin: e17e38d6f504857748a3ec1299d8a2bbeeeea854
|
||||
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
|
||||
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
|
||||
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
|
||||
media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
|
||||
media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
|
||||
@ -226,21 +238,22 @@ SPEC CHECKSUMS:
|
||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||
photo_manager: 1d80ae07a89a67dfbcae95953a1e5a24af7c3e62
|
||||
record_ios: fee1c924aa4879b882ebca2b4bce6011bcfc3d8b
|
||||
SDWebImage: 73c6079366fea25fa4bb9640d5fb58f0893facd8
|
||||
SDWebImage: 9f177d83116802728e122410fb25ad88f5c7608a
|
||||
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
|
||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||
tencent_cloud_chat_push: f87ae58098c2062b06e81f39fc53afc528395916
|
||||
tencent_cloud_chat_sdk: 0a406f1854a65aad2f853494c02a2e084a027ab2
|
||||
TIMPush: d0dfe96355ee413a7cacb2576f8aaa66f6073ab2
|
||||
TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654
|
||||
TXIMSDK_Plus_iOS_XCFramework: cb54f7de6e30e1368c6831c6eff31c25393bbb98
|
||||
TXIMSDK_Plus_iOS_XCFramework: 36ea2cdb544315d79e520cd54d0bbb1fbb1110bc
|
||||
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
|
||||
video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b
|
||||
video_thumbnail: b637e0ad5f588ca9945f6e2c927f73a69a661140
|
||||
volume_controller: 3657a1f65bedb98fa41ff7dc5793537919f31b12
|
||||
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
|
||||
WechatOpenSDK-XCFramework: 36fb2bea0754266c17184adf4963d7e6ff98b69f
|
||||
webview_flutter_wkwebview: 1821ceac936eba6f7984d89a9f3bcb4dea99ebb2
|
||||
WechatOpenSDK-XCFramework: b072030c9eeee91dfff1856a7846f70f7b9a88ed
|
||||
|
||||
PODFILE CHECKSUM: 866435f3a12ad92d8fb66fa46b52776da7e16ce5
|
||||
PODFILE CHECKSUM: f650ed3bb2b12d7ea6a8477a748641b3d65c4991
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
||||
@ -15,8 +15,7 @@
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
C8092B212E34A78000D25A0B /* WechatOpenSDK-XCFramework.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8092B202E34A78000D25A0B /* WechatOpenSDK-XCFramework.xcframework */; };
|
||||
C8092B222E34A78000D25A0B /* WechatOpenSDK-XCFramework.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C8092B202E34A78000D25A0B /* WechatOpenSDK-XCFramework.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
C8F379732E85F00600E7B665 /* WechatOpenSDK.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8F379722E85F00600E7B665 /* WechatOpenSDK.xcframework */; };
|
||||
ECDFBB33253E89949730F7D8 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 898AE91CA73F2F6E910D884D /* Pods_Runner.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@ -37,7 +36,6 @@
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
C8092B222E34A78000D25A0B /* WechatOpenSDK-XCFramework.xcframework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -66,8 +64,8 @@
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
A9774DDA95C7FD895F6925A4 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
BB84C2FA9C50ACAF0C376254 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
C8092B202E34A78000D25A0B /* WechatOpenSDK-XCFramework.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = "WechatOpenSDK-XCFramework.xcframework"; path = "Pods/WechatOpenSDK-XCFramework/WechatOpenSDK-XCFramework.xcframework"; sourceTree = "<group>"; };
|
||||
C891EF1D2E43F9730021EB39 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
|
||||
C8F379722E85F00600E7B665 /* WechatOpenSDK.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = WechatOpenSDK.xcframework; path = "Pods/WechatOpenSDK-XCFramework/WechatOpenSDK.xcframework"; sourceTree = "<group>"; };
|
||||
DCA23AF172275D04ECB63EFB /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
E3EC116A6CCDD06C6D4615E2 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
@ -77,7 +75,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C8092B212E34A78000D25A0B /* WechatOpenSDK-XCFramework.xcframework in Frameworks */,
|
||||
C8F379732E85F00600E7B665 /* WechatOpenSDK.xcframework in Frameworks */,
|
||||
ECDFBB33253E89949730F7D8 /* Pods_Runner.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -165,7 +163,7 @@
|
||||
9CE4C2341F34F9A85A1D3EED /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C8092B202E34A78000D25A0B /* WechatOpenSDK-XCFramework.xcframework */,
|
||||
C8F379722E85F00600E7B665 /* WechatOpenSDK.xcframework */,
|
||||
898AE91CA73F2F6E910D884D /* Pods_Runner.framework */,
|
||||
1AE799326ED7557212A901E0 /* Pods_RunnerTests.framework */,
|
||||
);
|
||||
@ -510,8 +508,13 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = cn.net.wzj.mall;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Profile;
|
||||
@ -689,6 +692,54 @@
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = 9C9VWBX77X;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/HydraAsync\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/Mantle\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/SDWebImageWebPCoder\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/TOCropViewController\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/app_links\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/audioplayers_darwin\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/device_info_plus\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_image_compress_common\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_native_splash\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/flutter_upgrader\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/fluwx\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/geolocator_apple\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/image_cropper\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/image_gallery_saver_plus\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/image_picker_ios\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/install_plugin\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/libwebp\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/media_kit_libs_ios_video\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/media_kit_video\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/mobile_scanner\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/package_info_plus\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/path_provider_foundation\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/permission_handler_apple\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/photo_manager\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/record_ios\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/sqflite_darwin\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/tencent_cloud_chat_push\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/tencent_cloud_chat_sdk\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/url_launcher_ios\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/video_player_avfoundation\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/video_thumbnail\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/volume_controller\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/wakelock_plus\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/webview_flutter_wkwebview\"",
|
||||
"\"${PODS_ROOT}/../.symlinks/plugins/media_kit_libs_ios_video/ios/Frameworks\"",
|
||||
"\"${PODS_ROOT}/../.symlinks/plugins/tencent_cloud_chat_sdk/ios/Frameworks\"",
|
||||
"\"${PODS_ROOT}/TIMPush\"",
|
||||
"\"${PODS_ROOT}/TXIMSDK_Plus_iOS_XCFramework\"",
|
||||
"\"${PODS_ROOT}/WechatOpenSDK-XCFramework\"",
|
||||
"\"${PODS_XCFRAMEWORKS_BUILD_DIR}/TIMPush\"",
|
||||
"\"${PODS_XCFRAMEWORKS_BUILD_DIR}/TXIMSDK_Plus_iOS_XCFramework\"",
|
||||
"\"${PODS_XCFRAMEWORKS_BUILD_DIR}/WechatOpenSDK-XCFramework\"/**",
|
||||
"\"${PODS_XCFRAMEWORKS_BUILD_DIR}/media_kit_libs_ios_video\"",
|
||||
"\"${PODS_XCFRAMEWORKS_BUILD_DIR}/tencent_cloud_chat_sdk\"",
|
||||
);
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@ -698,9 +749,14 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = cn.net.wzj.mall;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
@ -726,8 +782,13 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = cn.net.wzj.mall;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
|
||||
@ -5,17 +5,50 @@ import Flutter
|
||||
import TIMPush
|
||||
import tencent_cloud_chat_push
|
||||
|
||||
// 官方微信 SDK
|
||||
import WechatOpenSDK
|
||||
|
||||
|
||||
// Add `, TIMPushDelegate` to the following line
|
||||
@UIApplicationMain
|
||||
@objc class AppDelegate: FlutterAppDelegate, TIMPushDelegate {
|
||||
private let CHANNEL = "wechat_business_view"
|
||||
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
// 集成商家转账用户确认收款
|
||||
let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
|
||||
let methodChannel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: controller.binaryMessenger)
|
||||
|
||||
methodChannel.setMethodCallHandler { [weak self] (call, result) in
|
||||
if call.method == "openBusinessView" {
|
||||
if let args = call.arguments as? [String: Any],
|
||||
let packageInfo = args["package"] as? String {
|
||||
self?.openBusinessView(packageInfo: packageInfo)
|
||||
result(true)
|
||||
} else {
|
||||
result(FlutterError(code: "BAD_ARGS", message: "Missing package info", details: nil))
|
||||
}
|
||||
} else {
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
//end
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
|
||||
private func openBusinessView(packageInfo: String) {
|
||||
let req = WXOpenBusinessViewReq()
|
||||
req.businessType = "requestMerchantTransfer"
|
||||
req.query = packageInfo
|
||||
WXApi.send(req) { success in
|
||||
print("WXOpenBusinessViewReq send result: \(success)")
|
||||
}
|
||||
}
|
||||
|
||||
// To be deprecated,please use the new field businessID below.
|
||||
@objc func offlinePushCertificateID() -> Int32 {
|
||||
return TencentCloudChatPushFlutterModal.shared.offlinePushCertificateID();
|
||||
|
||||
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 69 B |
|
Before Width: | Height: | Size: 536 KiB After Width: | Height: | Size: 69 B |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 69 B |
@ -38,7 +38,7 @@
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="LaunchImage" width="1024" height="1536"/>
|
||||
<image name="LaunchImage" width="168" height="185"/>
|
||||
<image name="LaunchBackground" width="1" height="1"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
@ -1,97 +1,112 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>无终街</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>loopin</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>weixin</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>wxebcdaea31881caab</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>weixin</string>
|
||||
<string>wechat</string>
|
||||
<string>weixinULAPI</string>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>无终街</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>loopin</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>weixin</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>wxebcdaea31881caab</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>startApp</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>wuzhongjie</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>weixin</string>
|
||||
<string>wechat</string>
|
||||
<string>weixinULAPI</string>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>App需要使用您的相机进行拍摄</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>App需要访问麦克风用于发送语音消息</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>App需要权限以保存图片或视频到您的相册</string>
|
||||
<key>NSPhotoLibraryLimitedUsageDescription</key>
|
||||
<string>App需要访问部分照片用于选择图片或视频</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>App需要访问您的相册用于选择图片或视频</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<true/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>com.apple.developer.associated-domains</key>
|
||||
<array>
|
||||
<string>applinks:wuzhongjie.com.cn</string>
|
||||
</array>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>weixin</string>
|
||||
<string>weixinULAPI</string>
|
||||
<string>weixinURLParamsAPI</string>
|
||||
</array>
|
||||
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>App需要使用您的相机进行拍摄</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>App需要访问麦克风用于发送语音消息</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>App需要权限以保存图片或视频到您的相册</string>
|
||||
<key>NSPhotoLibraryLimitedUsageDescription</key>
|
||||
<string>App需要访问部分照片用于选择图片或视频</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>App需要访问您的相册用于选择图片或视频</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen.storyboard</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<true/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
|
||||
<key>com.apple.developer.associated-domains</key>
|
||||
<array>
|
||||
<string>applinks:wuzhongjie.com.cn</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -147,9 +147,9 @@ class ChatController extends GetxController {
|
||||
}
|
||||
|
||||
final List<ConversationViewModel> convList = res.data;
|
||||
for (var conv in convList) {
|
||||
logger.w('基本会话: ${conv.conversation.toJson()}');
|
||||
}
|
||||
// for (var conv in convList) {
|
||||
// logger.w('基本会话: ${conv.conversation.toJson()}');
|
||||
// }
|
||||
// 去重
|
||||
// 已有会话ID集合
|
||||
final existingIds = chatList.map((e) => e.conversation.conversationID).where((id) => id.isNotEmpty).toSet();
|
||||
|
||||
@ -9,6 +9,13 @@ class ImUserInfoController extends GetxController {
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
logger.i('开始IM用户信息初始化');
|
||||
init(V2TimUserFullInfo(customInfo: {
|
||||
"coverBg": "",
|
||||
"area": "",
|
||||
"areaCode": "",
|
||||
"openId": "",
|
||||
"tag": "",
|
||||
}));
|
||||
}
|
||||
|
||||
V2TimUserFullInfo? rawUserInfo;
|
||||
@ -47,6 +54,19 @@ class ImUserInfoController extends GetxController {
|
||||
"openId": "",
|
||||
"tag": "",
|
||||
});
|
||||
// final safeMap = <String, String>{};
|
||||
// userInfo.customInfo?.forEach((key, value) {
|
||||
// safeMap[key] = value;
|
||||
// });
|
||||
|
||||
// customInfo.assignAll({
|
||||
// "coverBg": "",
|
||||
// "area": "",
|
||||
// "areaCode": "",
|
||||
// "openId": "",
|
||||
// "tag": "",
|
||||
// ...safeMap, // 覆盖默认值
|
||||
// });
|
||||
|
||||
role.value = userInfo.role ?? 0;
|
||||
level.value = userInfo.level ?? 0;
|
||||
|
||||
@ -23,7 +23,12 @@ class ImCore {
|
||||
// 初始化视频
|
||||
final videoController = Get.find<VideoModuleController>();
|
||||
videoController.init();
|
||||
Get.toNamed('/login');
|
||||
Get.offAllNamed(
|
||||
'/login',
|
||||
predicate: (route) {
|
||||
return route.settings.name == '/';
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +1,17 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||
import 'package:loopin/IM/im_message.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/models/conversation_view_model.dart';
|
||||
import 'package:loopin/pages/groupChat/controller/group_detail_controller.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimGroupListener.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_change_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_change_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_topic_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_manager.dart';
|
||||
|
||||
@ -130,19 +137,67 @@ class ImGroupListeners {
|
||||
}
|
||||
|
||||
// 成员被邀请入群
|
||||
void onMemberInvited(String groupID, List<V2TimGroupMemberInfo> memberList) {
|
||||
void onMemberInvited(String groupID, List<V2TimGroupMemberInfo> memberList) async {
|
||||
if (Get.isRegistered<GroupDetailController>()) {
|
||||
final ctl = Get.find<GroupDetailController>();
|
||||
ctl.init();
|
||||
}
|
||||
//在群标记,目的为了清除被踢后的标记
|
||||
await ImService.instance.setConversationCustomData(customData: 'int', conversationIDList: ['group_$groupID']);
|
||||
}
|
||||
|
||||
// 成员被踢出群
|
||||
void onMemberKicked(String groupID, List<V2TimGroupMemberInfo> memberList) {
|
||||
void onMemberKicked(String groupID, List<V2TimGroupMemberInfo> memberList) async {
|
||||
if (Get.isRegistered<GroupDetailController>()) {
|
||||
final ctl = Get.find<GroupDetailController>();
|
||||
ctl.init();
|
||||
}
|
||||
// 这里处理会话
|
||||
final infoCtl = Get.find<ImUserInfoController>();
|
||||
final hasSelf = memberList.any((m) => m.userID == infoCtl.userID.value);
|
||||
logger.e(hasSelf);
|
||||
if (hasSelf) {
|
||||
// 给群打上标记
|
||||
await ImService.instance.setConversationCustomData(customData: 'out', conversationIDList: ['group_$groupID']);
|
||||
|
||||
// 被踢的人中包含自己,先在本地找会话,没有在去云端找
|
||||
final chatCtl = Get.find<ChatController>();
|
||||
ConversationViewModel? conv;
|
||||
try {
|
||||
conv = chatCtl.chatList.firstWhere((m) => m.conversation.groupID == groupID);
|
||||
} catch (e) {
|
||||
conv = null;
|
||||
}
|
||||
if (conv != null) {
|
||||
logger.e('本地找到');
|
||||
// 本地有 9=群tips
|
||||
final result = await IMMessage().createTextMessage(text: '你已被移出群聊');
|
||||
if (result.success && result.data != null) {
|
||||
V2TimMessage msg = result.data!.messageInfo!;
|
||||
msg.cloudCustomData = 'tips';
|
||||
final index = chatCtl.chatList.indexOf(conv);
|
||||
if (index != -1) {
|
||||
chatCtl.chatList[index].conversation.lastMessage = msg;
|
||||
chatCtl.chatList.refresh();
|
||||
logger.e('刷新');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 本地没拉到,去云端获取,
|
||||
final cloudRes = await ImService.instance.getConversation(conversationID: 'group_$groupID');
|
||||
if (cloudRes.success && cloudRes.data != null) {
|
||||
V2TimConversation cloudConv = cloudRes.data;
|
||||
final result = await IMMessage().createTextMessage(text: '你已被移出群聊');
|
||||
if (result.success && result.data != null) {
|
||||
V2TimMessage msg = result.data!.messageInfo!;
|
||||
msg.cloudCustomData = 'tips';
|
||||
cloudConv.lastMessage = msg;
|
||||
final resModel = await ConversationViewModel.createConversationViewModel(convList: [cloudConv]);
|
||||
chatCtl.chatList.insertAll(0, resModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 成员信息变更
|
||||
|
||||
@ -84,13 +84,6 @@ class ImService {
|
||||
if (result.success) {
|
||||
logger.i("IM 登录成功:$userID");
|
||||
|
||||
// 初始化会话数据
|
||||
final ctl = Get.find<ChatController>();
|
||||
await ctl.getConversationList();
|
||||
|
||||
/// 初始化微信 SDK
|
||||
await Wxsdk.init();
|
||||
|
||||
// 注册用户信息(基本信息+自定义信息)
|
||||
if (!Get.isRegistered<ImUserInfoController>()) {
|
||||
final imInfo = Get.put(ImUserInfoController(), permanent: true);
|
||||
@ -99,6 +92,13 @@ class ImService {
|
||||
await Get.find<ImUserInfoController>().refreshUserInfo();
|
||||
}
|
||||
|
||||
// 初始化会话数据
|
||||
final ctl = Get.find<ChatController>();
|
||||
await ctl.getConversationList();
|
||||
|
||||
/// 初始化微信 SDK
|
||||
await Wxsdk.init();
|
||||
|
||||
// 登录成功后注册高级消息监听器
|
||||
final messageService = ImMessageListenerService();
|
||||
Get.put<ImMessageListenerService>(messageService, permanent: true);
|
||||
@ -275,7 +275,7 @@ class ImService {
|
||||
|
||||
// 提前收集所有需要批量查询的 userID 和 groupID
|
||||
for (var conv in convList) {
|
||||
logger.e('未过滤前到会话数据:${conv.toLogString()}');
|
||||
// logger.e('未过滤前到会话数据:${conv.toLogString()}');
|
||||
if (conv.userID != null) {
|
||||
userIDList.add(conv.userID!);
|
||||
} else if (conv.groupID != null) {
|
||||
@ -297,7 +297,7 @@ class ImService {
|
||||
|
||||
// 读取管理员标识
|
||||
final customInfo = user.customInfo;
|
||||
logger.w('自定义信息:${user.toJson()}');
|
||||
// logger.w('自定义信息:${user.toJson()}');
|
||||
if (customInfo != null) {
|
||||
isCustomAdmin[userId] = customInfo['admin'] ?? '0';
|
||||
}
|
||||
@ -504,6 +504,14 @@ class ImService {
|
||||
);
|
||||
}
|
||||
|
||||
/// 标记单聊消息为已读
|
||||
Future<ImResult> markC2CMessageAsReadResult({
|
||||
required String userID,
|
||||
}) async {
|
||||
final res = await TIMMessageManager.instance.markC2CMessageAsRead(userID: userID);
|
||||
return ImResult.wrapNoData(res);
|
||||
}
|
||||
|
||||
///
|
||||
/// 清理指定单聊会话的未读数
|
||||
///
|
||||
@ -646,6 +654,12 @@ class ImService {
|
||||
return ImResult.wrap(res);
|
||||
}
|
||||
|
||||
/// 拉黑列表
|
||||
Future<ImResult<List<V2TimFriendInfo>>> blackList() async {
|
||||
final res = await TIMFriendshipManager.instance.getBlackList();
|
||||
return ImResult.wrap(res);
|
||||
}
|
||||
|
||||
///获取好友列表
|
||||
Future<ImResult<List<V2TimFriendInfo>>> getFriendList() async {
|
||||
final res = await TIMFriendshipManager.instance.getFriendList();
|
||||
|
||||
@ -48,20 +48,25 @@ class PushService {
|
||||
}
|
||||
|
||||
// 注册推送(初始化)
|
||||
// if (Platform.isAndroid) {
|
||||
// await compute(_registerPushInIsolate, {
|
||||
// 'sdkAppId': sdkAppId,
|
||||
// 'appKey': appKey,
|
||||
// 'apnsCertificateID': apnsCertificateID,
|
||||
// });
|
||||
// } else {
|
||||
await TencentCloudChatPush().registerPush(
|
||||
onNotificationClicked: _onNotificationClicked,
|
||||
sdkAppId: sdkAppId,
|
||||
appKey: appKey,
|
||||
apnsCertificateID: apnsCertificateID,
|
||||
);
|
||||
// }
|
||||
if (Platform.isAndroid) {
|
||||
// await compute(_registerPushInIsolate, {
|
||||
// 'sdkAppId': sdkAppId,
|
||||
// 'appKey': appKey,
|
||||
// 'apnsCertificateID': apnsCertificateID,
|
||||
// });
|
||||
await _registerPushInIsolate({
|
||||
'sdkAppId': sdkAppId,
|
||||
'appKey': appKey,
|
||||
'apnsCertificateID': apnsCertificateID,
|
||||
});
|
||||
} else {
|
||||
await TencentCloudChatPush().registerPush(
|
||||
onNotificationClicked: _onNotificationClicked,
|
||||
sdkAppId: sdkAppId,
|
||||
appKey: appKey,
|
||||
apnsCertificateID: apnsCertificateID,
|
||||
);
|
||||
}
|
||||
|
||||
// 关闭 App 在前台时弹出通知栏
|
||||
await TencentCloudChatPush().disablePostNotificationInForeground(disable: true);
|
||||
@ -70,8 +75,11 @@ class PushService {
|
||||
if (Platform.isAndroid) {
|
||||
await TencentImSDKPlugin.v2TIMManager.login(userID: Storage.read('userId'), userSig: Storage.read('userSig'));
|
||||
}
|
||||
final token = await TencentCloudChatPush().getAndroidPushToken();
|
||||
|
||||
logger.i('推送服务已注册,手机:$devices,证书ID:$apnsCertificateID');
|
||||
logger.i(
|
||||
'推送服务已注册,手机:$devices,证书ID:$apnsCertificateID,token=${token.code},tk=${token.data}',
|
||||
);
|
||||
|
||||
// 添加在线时监听器
|
||||
_addPushListener();
|
||||
@ -123,19 +131,21 @@ class PushService {
|
||||
/// 统一处理跳转逻辑
|
||||
static void _handleNotificationClick(String ext, {String? userID, String? groupID}) async {
|
||||
try {
|
||||
// ext={id:对应业务ID,type:'newFocus',userID:发送人的id,groupID:群ID}
|
||||
// ext={id:对应业务ID,type:'newFocus',userID:发送人的id,groupID:群ID} // type=
|
||||
// final ext = jsonEncode({
|
||||
// "userID": "123456",
|
||||
// "groupID": "654321",
|
||||
// });
|
||||
final isGroup = groupID != null && groupID.isNotEmpty;
|
||||
|
||||
final data = jsonDecode(ext);
|
||||
logger.i(data);
|
||||
final type = data['type'];
|
||||
final router = conversationTypeFromString(type);
|
||||
// final type = data['type'];
|
||||
final router = conversationTypeFromString(userID); //这里就是解析用于发送消息的管理员的userID
|
||||
logger.w(router);
|
||||
if (router == null || router != '') {
|
||||
if (router == null) {
|
||||
// 聊天
|
||||
if (data['userID'] != null) {
|
||||
if (!isGroup) {
|
||||
logger.w('有userID');
|
||||
// 单聊,获取会话
|
||||
final covRes = await ImService.instance.getConversation(conversationID: 'c2c_${data['userID']}');
|
||||
@ -160,7 +170,9 @@ class PushService {
|
||||
}
|
||||
} else {
|
||||
// 通知类相关(这里的ID只在特定业务场景才有,比如用户发起退款,要推送给商家,这里的id对应的就是订单id)
|
||||
Get.toNamed('/$router', arguments: data['id'] ?? '');
|
||||
final notifyRes = await ImService.instance.getConversation(conversationID: 'c2c_$router');
|
||||
final V2TimConversation notifyConversation = notifyRes.data;
|
||||
Get.toNamed('/$router', arguments: notifyConversation);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.i("[推送点击] ext 解析失败: $e");
|
||||
|
||||
@ -4,7 +4,10 @@ class CommonApi {
|
||||
// /app/member/sms/code
|
||||
static const String getDelCode = '/app/member/sms/code'; // 发送短信验证码 {'templateId'}
|
||||
|
||||
static const String accountInfo = '/app/member/info'; // 账户信息
|
||||
static const String accountInfo = '/app/member/info'; // 账户基本信息
|
||||
|
||||
/// 用户钱包,积分,商家资料信息 /app/member/account
|
||||
static const String userAccount = '/app/member/account';
|
||||
|
||||
///---------post
|
||||
static const String login = '/auth/login'; // 登录 {'phonenumber': '', 'smsCode': '', 'clientId': '428a8310cd442757ae699df5d894f051', 'grantType': 'sms'};
|
||||
@ -22,13 +25,13 @@ class CommonApi {
|
||||
|
||||
///余额充值 [money]金额
|
||||
// {
|
||||
// "orderType": "RECHARGE",
|
||||
// "orderType": "RECHARGE","ORDER"
|
||||
// "clientType": "APP",
|
||||
// "paymentMethod": "WECHAT",
|
||||
// "paymentClient": "APP",
|
||||
// "money": 100
|
||||
// }
|
||||
static const String addBalance = '/app/payment/pay';
|
||||
static const String wechatPay = '/app/payment/pay';
|
||||
|
||||
///余额提现
|
||||
// {
|
||||
@ -40,5 +43,21 @@ class CommonApi {
|
||||
///注销 /app/member/revoked
|
||||
static const String revoked = '/app/member/revoked';
|
||||
|
||||
///resource/oss/upload
|
||||
/// 获取账单信息 [source]=3
|
||||
static const String bills = '/app/member/bills';
|
||||
|
||||
/// 发送红包 /api/red-packet/send
|
||||
// "chatType": 1, 1=单 2=群
|
||||
// "senderId": 1950059660646047745, 发送人
|
||||
// "receiverId": 1949004087674580994, 接收人 单聊时传
|
||||
// "groupId": "", 群聊传
|
||||
// "totalAmount": 1,
|
||||
// "totalCount": 1,
|
||||
// "remark": "恭喜发财"
|
||||
static const String redpacket = '/app/customer/packet/send';
|
||||
|
||||
/// 抢红包 /api/red-packet/grab
|
||||
// "packetId": "1954790726899544065",
|
||||
// "memberId": "1949004087674580994"
|
||||
static const String getpacket = '/app/customer/packet/grab';
|
||||
}
|
||||
|
||||
@ -5,6 +5,8 @@ class VideoApi {
|
||||
static const String friendList = '/app/vlog/friendList'; //互关好友的视频
|
||||
static const String followList = '/app/vlog/followList'; // 我关注的博主视频
|
||||
static const String detail = '/app/vlog/detail'; // 视频详情
|
||||
static const String deleteVideo = '/app/vlog/delete'; //删除当前用户的视频
|
||||
|
||||
// post
|
||||
static const String myPublicList = '/app/vlog/myPublicList'; // 我发布的视频
|
||||
static const String myLikedList = '/app/vlog/myLikedList'; // 我点赞的视频
|
||||
@ -13,9 +15,6 @@ class VideoApi {
|
||||
static const String reportVideoApi = '/app/feedback/add'; // 投诉视频
|
||||
static const String videoDetailApi = '/app/vlog/detail/'; // 根据视频Id获取视频系详情
|
||||
|
||||
|
||||
|
||||
|
||||
static const String unlike = '/app/vlog/unlike'; //取消点赞
|
||||
static const String totalLikedCounts = '/app/vlog/totalLikedCounts'; //收到点赞总数
|
||||
static const String publish = '/app/vlog/publish'; //发布视频
|
||||
@ -24,6 +23,5 @@ class VideoApi {
|
||||
static const String changeToPublic = '/app/vlog/changeToPublic'; //将视频改为公开状态
|
||||
static const String changeToPrivate = '/app/vlog/changeToPrivate'; //将视频改为私密状态
|
||||
|
||||
static const String deleteVideo = '/app/vlog/delete'; //删除当前用户的视频
|
||||
static const String getVideoListByMemberId = '/app/vlog/page'; //查询某人的作品
|
||||
}
|
||||
|
||||
@ -27,12 +27,14 @@ class NetworkOrAssetImage extends StatelessWidget {
|
||||
width: width,
|
||||
height: height,
|
||||
fit: fit,
|
||||
placeholder: (context, url) => Image.asset(
|
||||
placeholderAsset.isEmpty ? 'assets/images/avatar/default.png' : placeholderAsset,
|
||||
width: width,
|
||||
height: height,
|
||||
fit: fit,
|
||||
),
|
||||
placeholder: placeholderAsset.isNotEmpty
|
||||
? (context, url) => Image.asset(
|
||||
placeholderAsset.isEmpty ? 'assets/images/avatar/default.png' : placeholderAsset,
|
||||
width: width,
|
||||
height: height,
|
||||
fit: fit,
|
||||
)
|
||||
: null,
|
||||
errorWidget: (context, url, error) => Image.asset(
|
||||
placeholderAsset,
|
||||
width: width,
|
||||
|
||||
57
lib/components/web_page.dart
Normal file
@ -0,0 +1,57 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class WebPage extends StatefulWidget {
|
||||
final String url;
|
||||
final String title;
|
||||
const WebPage({super.key, required this.url, required this.title});
|
||||
|
||||
@override
|
||||
State<WebPage> createState() => _WebPageState();
|
||||
}
|
||||
|
||||
class _WebPageState extends State<WebPage> {
|
||||
late final WebViewController _controller;
|
||||
double _progress = 0.0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
onProgress: (progress) {
|
||||
setState(() {
|
||||
_progress = progress / 100.0;
|
||||
});
|
||||
},
|
||||
),
|
||||
)
|
||||
..loadRequest(Uri.parse(widget.url));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(widget.title),
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
onPressed: () async {
|
||||
if (await _controller.canGoBack()) {
|
||||
_controller.goBack();
|
||||
} else {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(2.0),
|
||||
child: _progress < 1.0 ? LinearProgressIndicator(value: _progress) : const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
body: WebViewWidget(controller: _controller),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -32,15 +32,15 @@ class _LayoutState extends State<Layout> {
|
||||
VideoModuleController videoModuleController = Get.put(VideoModuleController());
|
||||
|
||||
// page页面
|
||||
List pageList = [
|
||||
VideoPage(),
|
||||
IndexPage(),
|
||||
UploadVideoPage(
|
||||
visible: false,
|
||||
),
|
||||
ChatPage(),
|
||||
MyPage()
|
||||
];
|
||||
// List pageList = [
|
||||
// VideoPage(),
|
||||
// IndexPage(),
|
||||
// UploadVideoPage(
|
||||
// visible: false,
|
||||
// ),
|
||||
// ChatPage(),
|
||||
// MyPage(key: myPageKey)
|
||||
// ];
|
||||
// tabs选项
|
||||
List navItems = [
|
||||
BottomNavigationBarItem(icon: Icon(Icons.play_circle_outline), label: '视频'),
|
||||
@ -126,34 +126,62 @@ class _LayoutState extends State<Layout> {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.grey[50],
|
||||
// body: pageList[pageCurrent],
|
||||
body: Obx(() {
|
||||
return Stack(
|
||||
children: [
|
||||
Offstage(
|
||||
offstage: videoModuleController.layoutPageCurrent.value != 0,
|
||||
child: const KeepAliveWrapper(child: VideoPage()),
|
||||
),
|
||||
Offstage(
|
||||
offstage: videoModuleController.layoutPageCurrent.value != 1,
|
||||
child: IndexPage(), // 不需要保活
|
||||
),
|
||||
Offstage(
|
||||
offstage: videoModuleController.layoutPageCurrent.value != 2,
|
||||
child: UploadVideoPage(
|
||||
// body: Stack(
|
||||
// children: [
|
||||
// Obx(
|
||||
// () => Offstage(
|
||||
// offstage: videoModuleController.layoutPageCurrent.value != 0,
|
||||
// child: const KeepAliveWrapper(child: VideoPage()),
|
||||
// ),
|
||||
// ),
|
||||
// Obx(
|
||||
// () => Offstage(
|
||||
// offstage: videoModuleController.layoutPageCurrent.value != 1,
|
||||
// child: IndexPage(), // 商品保活
|
||||
// ),
|
||||
// ),
|
||||
// Obx(
|
||||
// () => Offstage(
|
||||
// offstage: videoModuleController.layoutPageCurrent.value != 2,
|
||||
// child: UploadVideoPage(
|
||||
// visible: videoModuleController.layoutPageCurrent.value == 2,
|
||||
// ), // 不需要保活
|
||||
// ),
|
||||
// ),
|
||||
// Obx(
|
||||
// () => Offstage(
|
||||
// offstage: videoModuleController.layoutPageCurrent.value != 3,
|
||||
// child: ChatPage(), // 不需要保活
|
||||
// ),
|
||||
// ),
|
||||
// Obx(
|
||||
// () => Offstage(
|
||||
// offstage: videoModuleController.layoutPageCurrent.value != 4,
|
||||
// child: MyPage(key: myPageKey), // 不需要保活
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
body: Obx(
|
||||
() {
|
||||
return IndexedStack(
|
||||
index: videoModuleController.layoutPageCurrent.value,
|
||||
children: [
|
||||
const KeepAliveWrapper(child: VideoPage()),
|
||||
const KeepAliveWrapper(child: IndexPage()),
|
||||
UploadVideoPage(
|
||||
visible: videoModuleController.layoutPageCurrent.value == 2,
|
||||
), // 不需要保活
|
||||
),
|
||||
Offstage(
|
||||
offstage: videoModuleController.layoutPageCurrent.value != 3,
|
||||
child: ChatPage(), // 不需要保活
|
||||
),
|
||||
Offstage(
|
||||
offstage: videoModuleController.layoutPageCurrent.value != 4,
|
||||
child: MyPage(key: myPageKey), // 不需要保活
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
const KeepAliveWrapper(child: ChatPage()),
|
||||
KeepAliveWrapper(
|
||||
child: MyPage(
|
||||
key: myPageKey,
|
||||
)),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
// 底部导航栏
|
||||
bottomNavigationBar: Theme(
|
||||
// Flutter去掉BottomNavigationBar底部导航栏的水波纹
|
||||
@ -241,6 +269,6 @@ class _LayoutState extends State<Layout> {
|
||||
myPageKey.currentState?.refreshData();
|
||||
}
|
||||
videoModuleController.updateLayoutPage(index);
|
||||
setState(() {});
|
||||
// setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,12 +7,13 @@ import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||
import 'package:loopin/IM/controller/tab_bar_controller.dart';
|
||||
import 'package:loopin/IM/im_core.dart' as im_core;
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/controller/shop_index_controller.dart';
|
||||
import 'package:loopin/service/http_config.dart';
|
||||
import 'package:loopin/utils/common.dart';
|
||||
import 'package:loopin/utils/deep_link_listener.dart';
|
||||
import 'package:loopin/utils/lifecycle_handler.dart';
|
||||
import 'package:loopin/utils/storage.dart';
|
||||
import 'package:media_kit/media_kit.dart';
|
||||
@ -24,18 +25,20 @@ import 'layouts/index.dart';
|
||||
import 'router/index.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// 注入tabbar
|
||||
Get.put(TabBarController(), permanent: true);
|
||||
// 注入会话列表
|
||||
Get.put(ChatController(), permanent: true);
|
||||
// 注入底导菜单易选的
|
||||
Get.put(ShopIndexController(), permanent: true);
|
||||
// Get.put(ShopIndexController(), permanent: true);
|
||||
// 用户基本信息模型
|
||||
Get.put(ImUserInfoController(), permanent: true);
|
||||
|
||||
// 监听app前后台状态
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
WidgetsBinding.instance.addObserver(LifecycleHandler());
|
||||
// 禁止横屏
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await SystemChrome.setPreferredOrientations([
|
||||
DeviceOrientation.portraitUp,
|
||||
DeviceOrientation.portraitDown,
|
||||
@ -86,35 +89,37 @@ class App extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetMaterialApp(
|
||||
title: '无终街',
|
||||
locale: const Locale('zh'),
|
||||
supportedLocales: const [
|
||||
Locale('zh'),
|
||||
],
|
||||
localizationsDelegates: [
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(
|
||||
// colorScheme: ColorScheme.fromSeed(seedColor: FStyle.primaryColor),
|
||||
useMaterial3: true,
|
||||
return DeepLinkListener(
|
||||
child: GetMaterialApp(
|
||||
title: '无终街',
|
||||
locale: const Locale('zh'),
|
||||
supportedLocales: const [
|
||||
Locale('zh'),
|
||||
],
|
||||
localizationsDelegates: [
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(
|
||||
// colorScheme: ColorScheme.fromSeed(seedColor: FStyle.primaryColor),
|
||||
useMaterial3: true,
|
||||
),
|
||||
home: const Layout(),
|
||||
initialRoute: '/',
|
||||
getPages: routePages,
|
||||
navigatorKey: MyDialog.navigatorKey,
|
||||
|
||||
// 注入 RouteObserver
|
||||
navigatorObservers: [routeObserver],
|
||||
|
||||
// 注入到 GetX
|
||||
builder: (context, child) {
|
||||
Get.put<RouteObserver<PageRoute>>(routeObserver);
|
||||
return child!;
|
||||
},
|
||||
),
|
||||
home: const Layout(),
|
||||
initialRoute: '/',
|
||||
getPages: routePages,
|
||||
navigatorKey: MyDialog.navigatorKey,
|
||||
|
||||
// 注入 RouteObserver
|
||||
navigatorObservers: [routeObserver],
|
||||
|
||||
// 注入到 GetX
|
||||
builder: (context, child) {
|
||||
Get.put<RouteObserver<PageRoute>>(routeObserver);
|
||||
return child!;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ enum NotifyMessageType {
|
||||
orderRecharge, //订单->充值 online
|
||||
orderPay, //订单->订单交易成功通知 online
|
||||
orderRefund, //订单->退款结果通知
|
||||
orderWithDraw, //订单->提现成功通知
|
||||
groupNotifyCheck, //群通知->进群申请 online
|
||||
groupNotifyAccpet, // 群通知->进群审核审核通过 online
|
||||
groupNotifyFail, // 群通知->进群审核审核拒绝 online
|
||||
@ -32,6 +33,7 @@ class NotifyMessageTypeConstants {
|
||||
static const String orderRecharge = 'orderRecharge';
|
||||
static const String orderPay = 'orderPay';
|
||||
static const String orderRefund = 'orderRefund';
|
||||
static const String orderWithDraw = 'orderWithDraw';
|
||||
static const String groupNotifyCheck = 'groupNotifyCheck';
|
||||
static const String groupNotifyAccpet = 'groupNotifyAccpet';
|
||||
static const String groupNotifyFail = 'groupNotifyFail';
|
||||
@ -63,6 +65,8 @@ extension NotifyMessageTypeExtension on NotifyMessageType {
|
||||
return 'orderRecharge';
|
||||
case NotifyMessageType.orderPay:
|
||||
return 'orderPay';
|
||||
case NotifyMessageType.orderWithDraw:
|
||||
return 'orderWithDraw';
|
||||
case NotifyMessageType.orderRefund:
|
||||
return 'orderRefund';
|
||||
case NotifyMessageType.groupNotifyCheck:
|
||||
@ -101,6 +105,8 @@ notifyMessageTypeFromString(String? type) {
|
||||
return NotifyMessageType.orderRecharge.name;
|
||||
case 'orderPay':
|
||||
return NotifyMessageType.orderPay.name;
|
||||
case 'orderWithDraw':
|
||||
return NotifyMessageType.orderWithDraw.name;
|
||||
case 'orderRefund':
|
||||
return NotifyMessageType.orderRefund.name;
|
||||
case 'groupNotifyCheck':
|
||||
|
||||
@ -30,7 +30,7 @@ class _LoginState extends State<Login> {
|
||||
Timer? timer;
|
||||
String vcodeText = '获取验证码';
|
||||
bool disabled = false;
|
||||
int time = 6;
|
||||
int time = 60;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
||||
@ -8,22 +8,28 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:loopin/IM/controller/chat_detail_controller.dart';
|
||||
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||
import 'package:loopin/IM/im_message.dart';
|
||||
import 'package:loopin/IM/im_result.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/api/common_api.dart';
|
||||
import 'package:loopin/components/image_viewer.dart';
|
||||
import 'package:loopin/components/my_toast.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/components/preview_video.dart';
|
||||
import 'package:loopin/models/summary_type.dart';
|
||||
import 'package:loopin/pages/groupChat/controller/group_detail_controller.dart';
|
||||
import 'package:loopin/pages/groupChat/groupDetail.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/utils/audio_player_service.dart';
|
||||
import 'package:loopin/utils/parse_message_summary.dart';
|
||||
import 'package:loopin/utils/snapshot.dart';
|
||||
import 'package:loopin/utils/voice_service.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
import 'package:shirne_dialog/shirne_dialog.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/group_type.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
||||
@ -79,7 +85,7 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
||||
List chooseOptions = [
|
||||
{'key': 'photo', 'name': '相册', 'icon': 'assets/images/icon_photo.webp'},
|
||||
{'key': 'camera', 'name': '拍摄', 'icon': 'assets/images/icon_camera.webp'},
|
||||
{'key': 'location', 'name': '位置', 'icon': 'assets/images/icon_location.webp'},
|
||||
// {'key': 'location', 'name': '位置', 'icon': 'assets/images/icon_location.webp'},
|
||||
{'key': 'redpacket', 'name': '红包', 'icon': 'assets/images/icon_hb.webp'},
|
||||
];
|
||||
|
||||
@ -662,10 +668,16 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
||||
}
|
||||
// 红包模板=自定义=2;
|
||||
else if (item.elemType == 2 && item.cloudCustomData == SummaryType.hongbao) {
|
||||
// 定位
|
||||
final selfId = Get.find<ImUserInfoController>().userID.value;
|
||||
final obj = jsonDecode(item.customElem!.data!);
|
||||
final open = obj['open'] ?? false;
|
||||
final open = obj['open'] ?? false; // 默认未开启,总开关,直到消费完才改成true;
|
||||
final remark = obj['remark'];
|
||||
// final maxNum = obj['maxNum'];
|
||||
final totalCount = obj['totalCount']; // 红包数量
|
||||
String idsString = obj['ids'] ?? '';
|
||||
final ids = idsString.isEmpty ? <String>[] : idsString.split(',');
|
||||
// 自己是否抢过了
|
||||
final hasGet = ids.contains(selfId);
|
||||
msgtpl.add(RenderChatItem(
|
||||
data: item,
|
||||
child: Ink(
|
||||
@ -688,7 +700,7 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
open
|
||||
open || hasGet
|
||||
? Icon(Icons.check_circle, size: 32.0, color: Colors.white70)
|
||||
: Image.asset(
|
||||
'assets/images/hbico.png',
|
||||
@ -1151,17 +1163,50 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
||||
|
||||
// 发红包消息
|
||||
void sendHongbao(date) async {
|
||||
final amount = date['amount']; //用户输入的金额
|
||||
final totalAmount = date['amount']; //用户输入的金额
|
||||
final remark = date['remark']; //用户输入的留言
|
||||
final maxNum = date['maxNum']; //红包数量
|
||||
final totalCount = date['maxNum']; //红包数量
|
||||
|
||||
// 先检测可用余额
|
||||
final makeJson = jsonEncode({
|
||||
"amount": amount,
|
||||
|
||||
final makeData = {
|
||||
"chatType": "2",
|
||||
"totalAmount": totalAmount,
|
||||
"totalCount": totalCount,
|
||||
"groupId": arguments.groupID,
|
||||
"remark": remark,
|
||||
"maxNum": maxNum,
|
||||
"open": false,
|
||||
};
|
||||
|
||||
// 检测钱包余额
|
||||
final resB = await Http.get(CommonApi.userAccount);
|
||||
final resData = resB['data']['wallet']; // 余额
|
||||
if (resData == null) {
|
||||
// balance.value = double.parse(resData);
|
||||
MyToast().tip(title: '余额不足');
|
||||
return;
|
||||
}
|
||||
if (double.parse(totalAmount) > double.parse(resData)) {
|
||||
MyToast().tip(title: '余额不足');
|
||||
return;
|
||||
}
|
||||
final packRes = await Http.post(CommonApi.redpacket, data: makeData);
|
||||
logger.e('');
|
||||
logger.w('红包发送成功响应:$packRes');
|
||||
|
||||
final makeJson = jsonEncode({
|
||||
"packetId": packRes['data']['packetId'], // 红包id
|
||||
// "totalAmount": totalAmount,
|
||||
"totalAmount": packRes['data']['totalAmount'], //总金额
|
||||
"remark": remark,
|
||||
"totalCount": packRes['data']['totalCount'], // 数量
|
||||
// "totalCount": totalCount, // 数量
|
||||
"open": false, // 默认未开启
|
||||
"ids": '', //默认为空,记录抢红包人的数据,用,隔开
|
||||
});
|
||||
|
||||
logger.e('红包数据:$makeJson');
|
||||
logger.e('请求数据:$makeData');
|
||||
|
||||
final res = await IMMessage().createCustomMessage(data: makeJson);
|
||||
if (res.success && (res.data != null)) {
|
||||
final custMsg = res.data!.messageInfo;
|
||||
@ -1419,9 +1464,18 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
||||
context: context,
|
||||
builder: (context) {
|
||||
final obj = jsonDecode(data.customElem!.data!);
|
||||
final amount = obj['amount'];
|
||||
final remark = obj['remark'];
|
||||
final totalAmount = obj['totalAmount'];
|
||||
final remark = obj['remark']; // 默认未开启,总开关,直到消费完才改成true;
|
||||
final open = obj['open'] ?? false;
|
||||
logger.e('开了吗$open');
|
||||
final packetId = obj['packetId'];
|
||||
|
||||
final selfId = Get.find<ImUserInfoController>().userID.value;
|
||||
final totalCount = obj['totalCount'] ?? '0'; // 红包数量
|
||||
String idsString = obj['ids'] ?? '';
|
||||
final ids = idsString.isEmpty ? <String>[] : idsString.split(',');
|
||||
// 自己是否抢过了
|
||||
final hasGet = ids.contains(selfId);
|
||||
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
@ -1453,7 +1507,7 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Text(
|
||||
amount,
|
||||
totalAmount,
|
||||
style: const TextStyle(color: Color(0xFFFFF9C7), fontWeight: FontWeight.w500, fontSize: 20.0),
|
||||
),
|
||||
SizedBox(height: 20.0),
|
||||
@ -1464,46 +1518,92 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
||||
SizedBox(
|
||||
height: 100.0,
|
||||
),
|
||||
if (open == false && data.isSelf == false)
|
||||
AnimatedBuilder(
|
||||
animation: animTurns,
|
||||
builder: (context, child) {
|
||||
return Transform(
|
||||
transform: Matrix4.rotationY(animTurns.value),
|
||||
alignment: Alignment.center,
|
||||
child: FilledButton(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all(const Color(0xFFFFF9C7)),
|
||||
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
||||
minimumSize: WidgetStateProperty.all(const Size(80.0, 80.0)),
|
||||
shape: WidgetStateProperty.all(const CircleBorder()),
|
||||
elevation: WidgetStateProperty.all(3.0),
|
||||
if (open == false) ...[
|
||||
// 总开未结束,一定显示
|
||||
if (hasGet == false)
|
||||
// 自己没领过才显示
|
||||
AnimatedBuilder(
|
||||
animation: animTurns,
|
||||
builder: (context, child) {
|
||||
return Transform(
|
||||
transform: Matrix4.rotationY(animTurns.value),
|
||||
alignment: Alignment.center,
|
||||
child: FilledButton(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all(const Color(0xFFFFF9C7)),
|
||||
padding: WidgetStateProperty.all(EdgeInsets.zero),
|
||||
minimumSize: WidgetStateProperty.all(const Size(80.0, 80.0)),
|
||||
shape: WidgetStateProperty.all(const CircleBorder()),
|
||||
elevation: WidgetStateProperty.all(3.0),
|
||||
),
|
||||
child: Text(
|
||||
'开',
|
||||
style: TextStyle(color: Color(0xFF3B3B3B), fontSize: 28.0),
|
||||
),
|
||||
onPressed: () async {
|
||||
// 点击开红包,开始动画
|
||||
animController.repeat();
|
||||
// 执行抢红包结果查询,(群)展示抢红包人员信息,单不用管
|
||||
// 执行消费红包动作
|
||||
//--------
|
||||
// 成功后修改消息体
|
||||
final ctl = Get.find<ImUserInfoController>();
|
||||
logger.w(packetId);
|
||||
try {
|
||||
final res = await Http.post(CommonApi.getpacket, data: {"packetId": packetId, "memberId": ctl.userID.value});
|
||||
logger.e('开群红包$res');
|
||||
if (res != null) {
|
||||
// 是否还有可抢次数
|
||||
final canGetNum = totalCount - ids.length;
|
||||
if (canGetNum > 0) {
|
||||
// 还有可以抢的次数
|
||||
obj['totalCount'] = totalCount - 1;
|
||||
} else {
|
||||
// 消费完了
|
||||
obj['totalCount'] = 0;
|
||||
obj['open'] = true; //总开关标记为true
|
||||
}
|
||||
// 记录自己的id
|
||||
ids.add(selfId);
|
||||
obj['ids'] = ids.join(',');
|
||||
data.customElem!.data = jsonEncode(obj);
|
||||
// 修改消息体
|
||||
ImService.instance.modifyMessage(message: data);
|
||||
// 模拟开红包逻辑,1 秒后停止动画
|
||||
Future.delayed(Duration(seconds: 1), () {
|
||||
animController.stop();
|
||||
animController.reset();
|
||||
Get.back();
|
||||
MyToast().tip(
|
||||
title: '恭喜!抢到了${res['data']['receiveAmount']}元',
|
||||
type: 'success',
|
||||
position: 'center',
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// 接口报500红包被抢完,关闭红包弹窗
|
||||
obj['totalCount'] = 0;
|
||||
obj['open'] = true; //总开关标记为true
|
||||
data.customElem!.data = jsonEncode(obj);
|
||||
ImService.instance.modifyMessage(message: data);
|
||||
|
||||
Future.delayed(Duration(seconds: 1), () {
|
||||
animController.stop();
|
||||
animController.reset();
|
||||
Navigator.pop(context);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
logger.e(e);
|
||||
logger.e('开红包失败');
|
||||
Get.back();
|
||||
}
|
||||
},
|
||||
),
|
||||
child: Text(
|
||||
'开',
|
||||
style: TextStyle(color: Color(0xFF3B3B3B), fontSize: 28.0),
|
||||
),
|
||||
onPressed: () async {
|
||||
// 点击开红包,开始动画
|
||||
animController.repeat();
|
||||
// 执行抢红包结果查询,(群)展示抢红包人员信息,单不用管
|
||||
// 执行消费红包动作
|
||||
//--------
|
||||
// 成功后修改消息体
|
||||
obj['open'] = true; //成功标记为true
|
||||
data.customElem!.data = jsonEncode(obj);
|
||||
ImService.instance.modifyMessage(message: data);
|
||||
// 模拟开红包逻辑,1 秒后停止动画
|
||||
Future.delayed(Duration(seconds: 1), () {
|
||||
animController.stop();
|
||||
animController.reset();
|
||||
Get.back();
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -1565,7 +1665,14 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
||||
}
|
||||
|
||||
// 发群红包弹窗
|
||||
void sendRedPacketDialog() {
|
||||
void sendRedPacketDialog() async {
|
||||
final max = await getGroupMaxNumber();
|
||||
if (max == -1) {
|
||||
MyToast().tip(title: '获取群资料失败');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
showModalBottomSheet(
|
||||
backgroundColor: Colors.grey[50],
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(15.0))),
|
||||
@ -1578,14 +1685,30 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return RedPacket(
|
||||
flag: true,
|
||||
onSend: (date) {
|
||||
sendHongbao(date);
|
||||
});
|
||||
flag: true,
|
||||
maxNum: max,
|
||||
onSend: (date) {
|
||||
sendHongbao(date);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// 获取群资料
|
||||
Future<int> getGroupMaxNumber() async {
|
||||
final res = await ImService.instance.getGroupsInfo(groupIDList: [arguments.groupID!]);
|
||||
if (res.success && res.data != null) {
|
||||
V2TimGroupInfo info = res.data!.first.groupInfo ?? V2TimGroupInfo(groupID: '', groupType: GroupType.Work);
|
||||
if (info.groupID.isEmpty) {
|
||||
return -1;
|
||||
}
|
||||
return info.memberCount ?? -1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> isInGroup() async {
|
||||
final res = await ImService.instance.getJoinedGroupList();
|
||||
if (res.success && res.data != null) {
|
||||
|
||||
@ -77,7 +77,7 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
|
||||
List chooseOptions = [
|
||||
{'key': 'photo', 'name': '相册', 'icon': 'assets/images/icon_photo.webp'},
|
||||
{'key': 'camera', 'name': '拍摄', 'icon': 'assets/images/icon_camera.webp'},
|
||||
{'key': 'location', 'name': '位置', 'icon': 'assets/images/icon_location.webp'},
|
||||
// {'key': 'location', 'name': '位置', 'icon': 'assets/images/icon_location.webp'},
|
||||
{'key': 'redpacket', 'name': '红包', 'icon': 'assets/images/icon_hb.webp'},
|
||||
];
|
||||
|
||||
|
||||
@ -3,12 +3,13 @@ library;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:loopin/components/my_toast.dart';
|
||||
import 'package:shirne_dialog/shirne_dialog.dart';
|
||||
|
||||
class RedPacket extends StatefulWidget {
|
||||
final bool flag; // true=群,false=单
|
||||
final void Function(Map<String, dynamic>)? onSend;
|
||||
final int? maxNum; // 红包最大数量
|
||||
final int? maxNum; // 限制红包最大数量
|
||||
const RedPacket({super.key, required this.flag, this.onSend, this.maxNum});
|
||||
|
||||
@override
|
||||
@ -22,6 +23,7 @@ class _RedPacketState extends State<RedPacket> {
|
||||
|
||||
String amount = '0.00';
|
||||
String remark = '恭喜发财,大吉大利';
|
||||
String outMax = ''; // 用户输入的数量
|
||||
// 限制只能输入数字和小数点,且最多两位小数
|
||||
final List<TextInputFormatter> _decimalInputFormatters = [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d{0,2}')),
|
||||
@ -58,7 +60,7 @@ class _RedPacketState extends State<RedPacket> {
|
||||
const Text('红包个数'),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
maxLength: widget.maxNum,
|
||||
maxLength: widget.maxNum ?? 1, //默认为1
|
||||
controller: _maxNumController,
|
||||
textAlign: TextAlign.right,
|
||||
buildCounter: (_, {required currentLength, maxLength, required isFocused}) => null, // 隐藏计数器
|
||||
@ -66,8 +68,19 @@ class _RedPacketState extends State<RedPacket> {
|
||||
hintText: "填写个数", isDense: true, hintStyle: TextStyle(fontSize: 14.0), border: OutlineInputBorder(borderSide: BorderSide.none)),
|
||||
onChanged: (value) {
|
||||
// 输入的红包个数
|
||||
if (value.isEmpty) return;
|
||||
final intValue = int.tryParse(value) ?? 0;
|
||||
if (intValue > (widget.maxNum ?? 1)) {
|
||||
// 超过最大值,回退到最大值
|
||||
_maxNumController.text = widget.maxNum.toString();
|
||||
MyToast().tip(title: '当前群内人数为${widget.maxNum}人', position: 'top');
|
||||
// 光标移到末尾
|
||||
_maxNumController.selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: _maxNumController.text.length),
|
||||
);
|
||||
}
|
||||
setState(() {
|
||||
remark = value;
|
||||
outMax = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
@ -169,12 +182,13 @@ class _RedPacketState extends State<RedPacket> {
|
||||
shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0))),
|
||||
),
|
||||
onPressed: () {
|
||||
FocusScope.of(context).unfocus();
|
||||
double amountDouble = double.tryParse(amount) ?? 0.0;
|
||||
if (amountDouble > 0) {
|
||||
//发送红包
|
||||
widget.onSend!(
|
||||
{
|
||||
'maxNum': widget.maxNum ?? 1,
|
||||
'maxNum': outMax.isEmpty ? '1' : outMax,
|
||||
'amount': amount,
|
||||
'remark': remark,
|
||||
},
|
||||
|
||||
249
lib/pages/chat/components/reportChat.dart
Normal file
@ -0,0 +1,249 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/api/video_api.dart';
|
||||
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||
import 'package:loopin/components/my_toast.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/utils/getCommonDictionary.dart';
|
||||
|
||||
class ReportChat extends StatefulWidget {
|
||||
final String convsationID;
|
||||
const ReportChat({super.key, required this.convsationID});
|
||||
|
||||
@override
|
||||
State<ReportChat> createState() => _ReportChatState();
|
||||
}
|
||||
|
||||
class _ReportChatState extends State<ReportChat> with SingleTickerProviderStateMixin {
|
||||
int? _selectedIndex;
|
||||
late dynamic args;
|
||||
final TextEditingController _reportController = TextEditingController();
|
||||
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
|
||||
List<dynamic> reasonTypeData = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
args = Get.arguments ?? {};
|
||||
getReportReasons('ums_chat_report'); // 获取投诉枚举
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_reportController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// 加载数据的方法
|
||||
Future<void> getReportReasons(String key) async {
|
||||
try {
|
||||
final data = await Commondictionary.getCommonDictionary(key);
|
||||
setState(() {
|
||||
reasonTypeData = data;
|
||||
});
|
||||
} catch (e) {
|
||||
print('加载失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// 举报
|
||||
Future<void> doReport(String dictValue, String reasonText) async {
|
||||
try {
|
||||
final res = await Http.post(VideoApi.reportVideoApi, data: {
|
||||
'type': '1', // 类型 1 举报 2 投诉 3 建议
|
||||
'content': reasonText,
|
||||
'aimId': widget.convsationID,
|
||||
'aimType': '5', // 1 会员 2 群组 3 评论 4 视频 5 聊天
|
||||
'reasonType': dictValue
|
||||
});
|
||||
if (res['code'] == 200) {
|
||||
// 投诉成功,返回视频页面
|
||||
MyToast().tip(
|
||||
title: '投诉成功',
|
||||
position: 'center',
|
||||
type: 'success',
|
||||
);
|
||||
Get.back(result: {'returnTo': '/'});
|
||||
} else {
|
||||
MyToast().tip(
|
||||
title: '投诉失败',
|
||||
position: 'center',
|
||||
type: 'error',
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
print('投诉失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
key: scaffoldKey,
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
elevation: 0,
|
||||
title: const Text(
|
||||
'举报',
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
body: ScrollConfiguration(
|
||||
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'您的举报我们将尽快受理,核实后我们将第一时间告知受理结果,请尽量提交完整的举报描述',
|
||||
style: TextStyle(fontSize: 13, color: Colors.grey, height: 1.4),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// 两列布局的投诉选项
|
||||
reasonTypeData.isEmpty
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 8,
|
||||
mainAxisSpacing: 2,
|
||||
childAspectRatio: 4,
|
||||
),
|
||||
itemCount: reasonTypeData.length,
|
||||
itemBuilder: (context, index) {
|
||||
return _buildRadioItem(index);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
const Text(
|
||||
'举报描述(选填)',
|
||||
style: TextStyle(fontSize: 14, color: Colors.black54, fontWeight: FontWeight.w500),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: TextField(
|
||||
controller: _reportController,
|
||||
maxLines: 5,
|
||||
maxLength: 32,
|
||||
cursorColor: Colors.black54,
|
||||
style: const TextStyle(
|
||||
color: Colors.black54,
|
||||
fontSize: 14,
|
||||
),
|
||||
decoration: const InputDecoration(
|
||||
contentPadding: EdgeInsets.all(12),
|
||||
border: InputBorder.none,
|
||||
hintText: '请输入内容',
|
||||
hintStyle: TextStyle(color: Colors.grey, fontSize: 13),
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text(
|
||||
'${_reportController.text.length}/32',
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: _selectedIndex != null ? _submitReport : null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
),
|
||||
child: const Text('提交', style: TextStyle(fontSize: 15)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRadioItem(int index) {
|
||||
final item = reasonTypeData[index];
|
||||
final isSelected = _selectedIndex == index;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_selectedIndex = index;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 20,
|
||||
height: 20,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: isSelected ? Colors.green : Colors.grey,
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
child: isSelected
|
||||
? Center(
|
||||
child: Container(
|
||||
width: 12,
|
||||
height: 12,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.green,
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
item['dictLabel'] ?? '未知原因',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: isSelected ? Colors.green : Colors.black54,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _submitReport() {
|
||||
if (_selectedIndex == null) {
|
||||
MyToast().tip(
|
||||
title: '请选择举报原因!',
|
||||
position: 'center',
|
||||
type: 'error',
|
||||
);
|
||||
return;
|
||||
}
|
||||
// 获取选中的原因
|
||||
final selectedReason = reasonTypeData[_selectedIndex!];
|
||||
final reasonText = _reportController.text;
|
||||
doReport(selectedReason['dictValue'], reasonText);
|
||||
}
|
||||
}
|
||||
@ -6,10 +6,12 @@ import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||
import 'package:loopin/IM/global_badge.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/api/shop_api.dart';
|
||||
import 'package:loopin/components/empty_tip.dart';
|
||||
import 'package:loopin/components/my_toast.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/components/scan_util.dart';
|
||||
import 'package:loopin/models/conversation_type.dart' as myConversationType;
|
||||
@ -69,9 +71,11 @@ class ChatPageState extends State<ChatPage> {
|
||||
|
||||
// 使用外部枚举的方法检查扫码类型
|
||||
final scanType = ScanCodeType.fromCode(code);
|
||||
|
||||
if (scanType != null) {
|
||||
final value = code.substring(scanType.prefix.length + 1); // 获取后缀值
|
||||
_processScanCode(scanType, value);
|
||||
// final value = code.substring(scanType.prefix.length + 1); // 获取后缀值
|
||||
// _processScanCode(scanType, value);
|
||||
_processScanCode(scanType, code);
|
||||
} else {
|
||||
// 未知类型的码
|
||||
Get.snackbar('未知的二维码类型', '无法识别此二维码: $code');
|
||||
@ -96,8 +100,16 @@ class ChatPageState extends State<ChatPage> {
|
||||
// 处理核销码
|
||||
void _handleVerificationCode(String value) async {
|
||||
print('处理核销码: $value');
|
||||
// 带着核销码,跳转到商家的商品详情页面,引导商家去手动点击核销按钮
|
||||
Get.toNamed('/sellerOrder/detail', arguments: {'writeOffCodeId': value});
|
||||
final controller = Get.find<ImUserInfoController>();
|
||||
final role = controller.role.value;
|
||||
final isSeller = Utils.hasRole(role, 2);
|
||||
if (isSeller) {
|
||||
// 带着核销码,跳转到商家的商品详情页面,引导商家去手动点击核销按钮
|
||||
Get.toNamed('/sellerOrder/detail', arguments: {'writeOffCodeId': value});
|
||||
} else {
|
||||
//
|
||||
MyToast().tip(title: '未入驻的商家不可操作');
|
||||
}
|
||||
}
|
||||
|
||||
// 处理好友码
|
||||
@ -315,19 +327,19 @@ class ChatPageState extends State<ChatPage> {
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem<String>(
|
||||
value: 'cs',
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.qr_code_scanner, color: Colors.white, size: 18),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
'测试',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// PopupMenuItem<String>(
|
||||
// value: 'cs',
|
||||
// child: Row(
|
||||
// children: [
|
||||
// Icon(Icons.qr_code_scanner, color: Colors.white, size: 18),
|
||||
// SizedBox(width: 8),
|
||||
// Text(
|
||||
// '测试',
|
||||
// style: TextStyle(color: Colors.white),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
);
|
||||
|
||||
@ -345,10 +357,11 @@ class ChatPageState extends State<ChatPage> {
|
||||
logger.w('点击了扫一扫');
|
||||
ScanUtil.openScanner(onResult: handleScanResult);
|
||||
break;
|
||||
case 'cs':
|
||||
logger.w('去互动');
|
||||
Get.toNamed('/newFoucs');
|
||||
break;
|
||||
// case 'cs':
|
||||
// logger.w('去互动');
|
||||
// // order newFoucs interaction
|
||||
// Get.toNamed('/interaction');
|
||||
// break;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -563,9 +576,10 @@ class ChatPageState extends State<ChatPage> {
|
||||
const SizedBox(height: 2.0),
|
||||
// 消息内容
|
||||
Text(
|
||||
chatList[index].conversation.lastMessage != null
|
||||
? parseMessageSummary(chatList[index].conversation.lastMessage!)
|
||||
: '',
|
||||
_handleLast(index),
|
||||
// chatList[index].conversation.lastMessage != null
|
||||
// ? parseMessageSummary(chatList[index].conversation.lastMessage!)
|
||||
// : '',
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 13.0),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
@ -660,4 +674,24 @@ class ChatPageState extends State<ChatPage> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _handleLast(index) {
|
||||
// logger.e('开始处理显示');
|
||||
|
||||
// 被踢出群处理
|
||||
// final outConv = controller.chatList.firstWhere(
|
||||
// (conv) => conv.conversation.customData == 'out',
|
||||
// orElse: () => ConversationViewModel(conversation: V2TimConversation(conversationID: '')),
|
||||
// );
|
||||
// logger.e('conv=${controller.chatList[index].conversation.toLogString()}');
|
||||
|
||||
if (controller.chatList[index].conversation.customData == 'out') {
|
||||
// logger.e('找到了');
|
||||
// 找到有标记的群,修改本地渲染
|
||||
return '你已被移出群聊';
|
||||
} else {
|
||||
final str = controller.chatList[index].conversation.lastMessage != null ? parseMessageSummary(controller.chatList[index].conversation.lastMessage!) : '';
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:loopin/IM/im_friend_listeners.dart';
|
||||
import 'package:loopin/components/my_qrcode.dart';
|
||||
import 'package:loopin/components/my_toast.dart';
|
||||
import 'package:loopin/components/scan_util.dart';
|
||||
import 'package:loopin/utils/index.dart';
|
||||
import 'package:loopin/utils/scan_code_type.dart';
|
||||
|
||||
class AddFriend extends StatefulWidget {
|
||||
@ -56,7 +57,17 @@ class _AddFriendState extends State<AddFriend> {
|
||||
void _handleVerificationCode(String value) async {
|
||||
logger.w('处理核销码: $value');
|
||||
// 带着核销码,跳转到商家的商品详情页面,引导商家去手动点击核销按钮
|
||||
Get.toNamed('/sellerOrder/detail', arguments: {'writeOffCodeId': value});
|
||||
final controller = Get.find<ImUserInfoController>();
|
||||
final role = controller.role.value;
|
||||
final isSeller = Utils.hasRole(role, 2);
|
||||
if (isSeller) {
|
||||
// 带着核销码,跳转到商家的商品详情页面,引导商家去手动点击核销按钮
|
||||
Get.toNamed('/sellerOrder/detail', arguments: {'writeOffCodeId': value});
|
||||
} else {
|
||||
//
|
||||
MyToast().tip(title: '无法识别');
|
||||
}
|
||||
// Get.toNamed('/sellerOrder/detail', arguments: {'writeOffCodeId': value});
|
||||
}
|
||||
|
||||
// 处理好友码
|
||||
|
||||
@ -7,11 +7,13 @@ import 'package:easy_refresh/easy_refresh.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/api/video_api.dart';
|
||||
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||
import 'package:loopin/components/empty_tip.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/models/conversation_type.dart';
|
||||
import 'package:loopin/models/notify_message.type.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/styles/index.dart';
|
||||
import 'package:loopin/utils/index.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||
@ -44,7 +46,7 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
|
||||
{'value': 'all', 'label': '全部', 'icon': Icons.all_inclusive},
|
||||
{'value': NotifyMessageTypeConstants.interactionLike, 'label': '点赞', 'icon': Icons.favorite_border},
|
||||
{'value': NotifyMessageTypeConstants.interactionComment, 'label': '评论', 'icon': Icons.comment},
|
||||
{'value': NotifyMessageTypeConstants.interactionAt, 'label': '@我', 'icon': Icons.alternate_email},
|
||||
// {'value': NotifyMessageTypeConstants.interactionAt, 'label': '@我', 'icon': Icons.alternate_email},
|
||||
{'value': NotifyMessageTypeConstants.interactionReply, 'label': '回复', 'icon': Icons.reply},
|
||||
];
|
||||
RxList demoList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].obs;
|
||||
@ -58,6 +60,7 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
|
||||
final lastmsg = conv?.lastMessage;
|
||||
if (lastmsg != null) {
|
||||
msgList.add(lastmsg);
|
||||
getMsgData();
|
||||
}
|
||||
}
|
||||
// chatController.addListener(() {
|
||||
@ -74,6 +77,18 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
|
||||
// });
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
cleanUnRead();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void cleanUnRead() async {
|
||||
if ((conv?.unreadCount ?? 0) > 0) {
|
||||
await ImService.instance.clearConversationUnreadCount(conversationID: conv?.conversationID ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
// 分页获取全部数据
|
||||
Future<void> getMsgData() async {
|
||||
// 获取最旧一条消息作为游标
|
||||
@ -260,14 +275,14 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
|
||||
childBuilder: (context, physics) {
|
||||
return Obx(
|
||||
() {
|
||||
// if (msgList.isEmpty) return EmptyTip();
|
||||
if (demoList.isEmpty) return EmptyTip();
|
||||
if (msgList.isEmpty) return EmptyTip();
|
||||
// if (demoList.isEmpty) return EmptyTip();
|
||||
return ListView.builder(
|
||||
// controller: chatController,
|
||||
shrinkWrap: true,
|
||||
physics: physics,
|
||||
// itemCount: msgList.length,
|
||||
itemCount: demoList.length,
|
||||
itemCount: msgList.length,
|
||||
// itemCount: demoList.length,
|
||||
itemBuilder: (context, index) {
|
||||
//检测cloudCustomData
|
||||
// interactionComment, //互动->评论 评论
|
||||
@ -276,17 +291,18 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
|
||||
// interactionReply, //互动->评论回复 评论
|
||||
|
||||
//----
|
||||
// final element =msgList[index].customElem!;
|
||||
// final cloudCustomData = msgList[index].cloudCustomData;
|
||||
// final desc = msgList[index].customElem!.desc!;
|
||||
// final jsonData = msgList[index].customElem!.data;
|
||||
|
||||
// ----测试数据
|
||||
final jsonData = '{"faceUrl":"","nickName":"测试昵称","userID":"213213"}';
|
||||
final element = msgList[index];
|
||||
// final element = msgList[index].customElem!;
|
||||
final cloudCustomData = msgList[index].cloudCustomData;
|
||||
final desc = msgList[index].customElem!.desc!;
|
||||
final jsonData = msgList[index].customElem!.data ?? '{"faceUrl":"","nickName":"data为null","userID":"213213"}';
|
||||
final item = jsonDecode(jsonData); // 数据
|
||||
final desc = '测试desc';
|
||||
final cloudCustomData = 'interactionLike';
|
||||
V2TimMessage element = V2TimMessage(elemType: 2, isRead: index > 2 ? true : false);
|
||||
// ----测试数据
|
||||
// final jsonData = '{"faceUrl":"","nickName":"测试昵称","userID":"213213"}';
|
||||
// final item = jsonDecode(jsonData); // 数据
|
||||
// final desc = '测试desc';
|
||||
// final cloudCustomData = 'interactionLike';
|
||||
// V2TimMessage element = V2TimMessage(elemType: 2, isRead: index > 2 ? true : false);
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
||||
@ -300,10 +316,9 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
|
||||
// 先获取视频详情
|
||||
// 如果cloudCustomData是interactionComment,interactionAt,interactionReply,传参时带上评论id,这三个是评论相关的
|
||||
//
|
||||
// final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
|
||||
final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
|
||||
// 此人存在才做跳转
|
||||
// Get.toNamed('/vloger', arguments: res['data']);
|
||||
Get.toNamed('/vloger');
|
||||
Get.toNamed('/vloger', arguments: res['data']);
|
||||
},
|
||||
child: ClipOval(
|
||||
child: NetworkOrAssetImage(
|
||||
@ -317,14 +332,14 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
|
||||
// 消息
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
onTap: () async {
|
||||
// 点击头像转到对方主页
|
||||
// 先获取视频详情
|
||||
// 如果cloudCustomData是interactionComment,interactionAt,interactionReply,传参时带上评论id,
|
||||
//
|
||||
// final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
|
||||
// Get.toNamed('/vloger', arguments: res['data']);
|
||||
Get.toNamed('/vloger');
|
||||
final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
|
||||
Get.toNamed('/vloger', arguments: res['data']);
|
||||
// Get.toNamed('/vloger');
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -343,8 +358,7 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||
// desc,
|
||||
'很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容',
|
||||
desc,
|
||||
),
|
||||
Text(
|
||||
Utils.formatTime(element.timestamp ?? DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
||||
|
||||
@ -10,6 +10,7 @@ import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/api/video_api.dart';
|
||||
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||
import 'package:loopin/components/empty_tip.dart';
|
||||
import 'package:loopin/components/my_toast.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/models/conversation_type.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
@ -48,10 +49,23 @@ class NewfoucsState extends State<Newfoucs> with SingleTickerProviderStateMixin
|
||||
final lastmsg = conv?.lastMessage;
|
||||
if (lastmsg != null) {
|
||||
msgList.add(lastmsg);
|
||||
getMsgData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
cleanUnRead();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void cleanUnRead() async {
|
||||
if ((conv?.unreadCount ?? 0) > 0) {
|
||||
await ImService.instance.clearConversationUnreadCount(conversationID: conv?.conversationID ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
// 分页获取全部数据
|
||||
Future<void> getMsgData() async {
|
||||
// 获取最旧一条消息作为游标
|
||||
@ -172,7 +186,7 @@ class NewfoucsState extends State<Newfoucs> with SingleTickerProviderStateMixin
|
||||
String? jsonData = msgList[index].customElem!.data;
|
||||
jsonData = (jsonData == null || jsonData.isEmpty) ? '{"faceUrl":"","nickName":"data为空","userID":"213213"}' : jsonData;
|
||||
|
||||
final item = jsonDecode(jsonData ?? '{"faceUrl":"","nickName":"测试昵称","userID":"213213"}');
|
||||
final item = jsonDecode(jsonData);
|
||||
|
||||
logger.w(element.toJson());
|
||||
|
||||
@ -197,8 +211,12 @@ class NewfoucsState extends State<Newfoucs> with SingleTickerProviderStateMixin
|
||||
// 如果cloudCustomData是interactionComment,interactionAt,interactionReply,传参时带上评论id,
|
||||
//
|
||||
final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
|
||||
Get.toNamed('/vloger', arguments: res['data']);
|
||||
// Get.toNamed('/vloger');
|
||||
if (res['data'] == null) {
|
||||
MyToast().tip(title: '视频数据不存在', position: 'top');
|
||||
} else {
|
||||
// 这里跟视频首页一样,跳转博主主页时传视频详情数据
|
||||
Get.toNamed('/vloger', arguments: res['data']);
|
||||
}
|
||||
},
|
||||
child: ClipOval(
|
||||
child: NetworkOrAssetImage(
|
||||
|
||||
@ -1,290 +0,0 @@
|
||||
/// 新关注通知
|
||||
library;
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:easy_refresh/easy_refresh.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/api/video_api.dart';
|
||||
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||
import 'package:loopin/components/empty_tip.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/models/conversation_type.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/styles/index.dart';
|
||||
import 'package:loopin/utils/index.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_custom_elem.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
|
||||
|
||||
// newFocus, 新的关注
|
||||
|
||||
class Newfoucs extends StatefulWidget {
|
||||
const Newfoucs({super.key});
|
||||
|
||||
@override
|
||||
State<Newfoucs> createState() => NewfoucsState();
|
||||
}
|
||||
|
||||
class NewfoucsState extends State<Newfoucs> with SingleTickerProviderStateMixin {
|
||||
bool isLoading = false; // 是否在加载中
|
||||
RxBool hasMore = true.obs; // 是否还有更多数据
|
||||
String page = '';
|
||||
|
||||
///-------------------
|
||||
V2TimConversation? conv;
|
||||
RxList<V2TimMessage> msgList = <V2TimMessage>[].obs;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (Get.arguments != null && Get.arguments is V2TimConversation) {
|
||||
// 如果有参数
|
||||
conv = Get.arguments as V2TimConversation;
|
||||
logger.e('lastmsg:$conv');
|
||||
|
||||
final lastmsg = conv?.lastMessage;
|
||||
if (lastmsg != null) {
|
||||
msgList.add(lastmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 分页获取全部数据
|
||||
Future<void> getMsgData() async {
|
||||
// 获取最旧一条消息作为游标
|
||||
V2TimMessage? lastRealMsg;
|
||||
lastRealMsg = msgList.last;
|
||||
final res = await ImService.instance.getHistoryMessageList(
|
||||
userID: ConversationType.newFocus.name, // userID为固定的newFocus
|
||||
lastMsg: lastRealMsg,
|
||||
);
|
||||
if (res.success && res.data != null) {
|
||||
msgList.addAll(res.data!);
|
||||
logger.e(msgList);
|
||||
if (res.data!.isEmpty) {
|
||||
hasMore.value = false;
|
||||
}
|
||||
logger.i('聊天数据加载成功');
|
||||
} else {
|
||||
logger.e('聊天数据加载失败:${res.desc}');
|
||||
}
|
||||
}
|
||||
|
||||
// 下拉刷新
|
||||
Future<void> handleRefresh() async {
|
||||
await Future.delayed(Duration(seconds: 5));
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.grey[50],
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
forceMaterialTransparency: true,
|
||||
bottom: PreferredSize(
|
||||
preferredSize: Size.fromHeight(1.0),
|
||||
child: Container(
|
||||
color: Colors.grey[300],
|
||||
height: 1.0,
|
||||
),
|
||||
),
|
||||
title: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'新的关注',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [],
|
||||
),
|
||||
body: ScrollConfiguration(
|
||||
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: EasyRefresh.builder(
|
||||
callLoadOverOffset: 20, //触底距离
|
||||
callRefreshOverOffset: 20, // 下拉距离
|
||||
header: ClassicHeader(
|
||||
dragText: '下拉刷新',
|
||||
armedText: '释放刷新',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: '加载完成',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
footer: ClassicFooter(
|
||||
dragText: '加载更多',
|
||||
armedText: '释放加载',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: '加载完成',
|
||||
noMoreText: '没有更多了~',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
onRefresh: () async {
|
||||
await handleRefresh();
|
||||
},
|
||||
onLoad: () async {
|
||||
if (hasMore.value) {
|
||||
await getMsgData();
|
||||
return hasMore.value ? IndicatorResult.success : IndicatorResult.noMore;
|
||||
}
|
||||
},
|
||||
childBuilder: (context, physics) {
|
||||
return Obx(
|
||||
() {
|
||||
if (msgList.isEmpty) return EmptyTip();
|
||||
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: physics,
|
||||
itemCount: msgList.length,
|
||||
itemBuilder: (context, index) {
|
||||
//检测cloudCustomData
|
||||
///发起关注人的[userID],
|
||||
///发起关注人的昵称[nickName],
|
||||
///发起关注人的头像地址[faceUrl],
|
||||
|
||||
//----正式数据
|
||||
V2TimMessage msg = msgList[index];
|
||||
V2TimCustomElem element = msgList[index].customElem!;
|
||||
final cloudCustomData = msgList[index].cloudCustomData;
|
||||
logger.w(cloudCustomData);
|
||||
final desc = msgList[index].customElem!.desc!;
|
||||
String? jsonData = msgList[index].customElem!.data;
|
||||
jsonData = (jsonData == null || jsonData.isEmpty) ? '{"faceUrl":"","nickName":"data为空","userID":"213213"}' : jsonData;
|
||||
|
||||
final item = jsonDecode(jsonData ?? '{"faceUrl":"","nickName":"测试昵称","userID":"213213"}');
|
||||
|
||||
logger.w(element.toJson());
|
||||
|
||||
// ----测试数据
|
||||
// final jsonData = '{"faceUrl":"","nickName":"测试昵称","userID":"213213"}';
|
||||
// final item = jsonDecode(jsonData); // 数据
|
||||
// final desc = '测试desc';
|
||||
// final cloudCustomData = 'newFocus';
|
||||
// V2TimCustomElem msg = V2TimMessage(elemType: 2, isRead: index > 2 ? true : false);
|
||||
// -----------
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
||||
child: Row(
|
||||
spacing: 10.0,
|
||||
children: <Widget>[
|
||||
// 头像
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
// 点击头像转到对方主页
|
||||
// 先获取视频详情
|
||||
// 如果cloudCustomData是interactionComment,interactionAt,interactionReply,传参时带上评论id,
|
||||
//
|
||||
final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
|
||||
Get.toNamed('/vloger', arguments: res['data']);
|
||||
// Get.toNamed('/vloger');
|
||||
},
|
||||
child: ClipOval(
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: item['faceUrl'],
|
||||
width: 50,
|
||||
height: 50,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 消息
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
// 点击头像转到对方主页,先获取视频详情
|
||||
final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
|
||||
Get.toNamed('/vloger', arguments: res['data']);
|
||||
// Get.toNamed('/vloger');
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
// 昵称
|
||||
Text(
|
||||
item['nickName'] ?? '未知',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2.0),
|
||||
// 描述内容
|
||||
Text(
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||
desc,
|
||||
// '很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容很长文本内容',
|
||||
),
|
||||
Text(
|
||||
Utils.formatTime(msg.timestamp ?? DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
||||
style: TextStyle(
|
||||
color: Colors.grey[600],
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 右侧
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Visibility(
|
||||
visible: false, // 新的关注不需要
|
||||
// 视频首图
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: item['firstFrameImg'],
|
||||
placeholderAsset: 'assets/images/bk.jpg',
|
||||
width: 40,
|
||||
height: 60,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5.0),
|
||||
// 角标
|
||||
Visibility(
|
||||
visible: !(msg.isRead ?? true),
|
||||
child: FStyle.badge(0, isdot: true),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
391
lib/pages/chat/notify/orderNotify.dart
Normal file
@ -0,0 +1,391 @@
|
||||
/// 订单通知
|
||||
library;
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:easy_refresh/easy_refresh.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||
import 'package:loopin/components/empty_tip.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/models/conversation_type.dart';
|
||||
import 'package:loopin/models/notify_message.type.dart';
|
||||
import 'package:loopin/pages/my/merchant/balance/balance.dart';
|
||||
import 'package:loopin/pages/my/merchant/balance/controller.dart';
|
||||
import 'package:loopin/styles/index.dart';
|
||||
import 'package:loopin/utils/index.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
|
||||
|
||||
// order, 订单
|
||||
|
||||
class OrderNotify extends StatefulWidget {
|
||||
const OrderNotify({super.key});
|
||||
|
||||
@override
|
||||
State<OrderNotify> createState() => OrderNotifyState();
|
||||
}
|
||||
|
||||
class OrderNotifyState extends State<OrderNotify> with SingleTickerProviderStateMixin {
|
||||
bool isLoading = false; // 是否在加载中
|
||||
RxBool hasMore = true.obs; // 是否还有更多数据
|
||||
String page = '';
|
||||
|
||||
///-------------------
|
||||
V2TimConversation? conv;
|
||||
RxList<V2TimMessage> msgList = <V2TimMessage>[].obs;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (Get.arguments != null && Get.arguments is V2TimConversation) {
|
||||
// 如果有参数
|
||||
conv = Get.arguments as V2TimConversation;
|
||||
logger.e('lastmsg:$conv');
|
||||
|
||||
final lastmsg = conv?.lastMessage;
|
||||
if (lastmsg != null) {
|
||||
msgList.add(lastmsg);
|
||||
getMsgData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
cleanUnRead();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void cleanUnRead() async {
|
||||
if ((conv?.unreadCount ?? 0) > 0) {
|
||||
await ImService.instance.clearConversationUnreadCount(conversationID: conv?.conversationID ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
// 分页获取全部数据
|
||||
Future<void> getMsgData() async {
|
||||
// 获取最旧一条消息作为游标
|
||||
V2TimMessage? lastRealMsg;
|
||||
lastRealMsg = msgList.last;
|
||||
final res = await ImService.instance.getHistoryMessageList(
|
||||
userID: ConversationType.order.name, // userID为固定的order
|
||||
lastMsg: lastRealMsg,
|
||||
);
|
||||
if (res.success && res.data != null) {
|
||||
msgList.addAll(res.data!);
|
||||
logger.e(msgList);
|
||||
if (res.data!.isEmpty) {
|
||||
hasMore.value = false;
|
||||
}
|
||||
logger.i('聊天数据加载成功');
|
||||
} else {
|
||||
logger.e('聊天数据加载失败:${res.desc}');
|
||||
}
|
||||
}
|
||||
|
||||
// 下拉刷新
|
||||
Future<void> handleRefresh() async {
|
||||
await Future.delayed(Duration(seconds: 5));
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.grey[50],
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
forceMaterialTransparency: true,
|
||||
bottom: PreferredSize(
|
||||
preferredSize: Size.fromHeight(1.0),
|
||||
child: Container(
|
||||
color: Colors.grey[300],
|
||||
height: 1.0,
|
||||
),
|
||||
),
|
||||
title: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'订单通知',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [],
|
||||
),
|
||||
body: ScrollConfiguration(
|
||||
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: EasyRefresh.builder(
|
||||
callLoadOverOffset: 20, //触底距离
|
||||
callRefreshOverOffset: 20, // 下拉距离
|
||||
header: ClassicHeader(
|
||||
dragText: '下拉刷新',
|
||||
armedText: '释放刷新',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: '加载完成',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
footer: ClassicFooter(
|
||||
dragText: '加载更多',
|
||||
armedText: '释放加载',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: '加载完成',
|
||||
noMoreText: '没有更多了~',
|
||||
failedText: '加载失败,请重试',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
onRefresh: () async {
|
||||
await handleRefresh();
|
||||
},
|
||||
onLoad: () async {
|
||||
if (hasMore.value) {
|
||||
await getMsgData();
|
||||
return hasMore.value ? IndicatorResult.success : IndicatorResult.noMore;
|
||||
}
|
||||
},
|
||||
childBuilder: (context, physics) {
|
||||
return Obx(
|
||||
() {
|
||||
if (msgList.isEmpty) return EmptyTip();
|
||||
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: physics,
|
||||
itemCount: msgList.length,
|
||||
itemBuilder: (context, index) {
|
||||
//检测cloudCustomData
|
||||
/// 订单编号[orderID]
|
||||
/// 订单金额[amount]
|
||||
/// 商品名称[name]
|
||||
/// 商品描述[describe]
|
||||
/// 商品价格[price]
|
||||
/// 商品主图[pic]
|
||||
|
||||
//----正式数据
|
||||
V2TimMessage msg = msgList[index];
|
||||
// V2TimCustomElem element = msgList[index].customElem;
|
||||
final cloudCustomData = msgList[index].cloudCustomData ?? '';
|
||||
logger.w(cloudCustomData);
|
||||
final desc = msgList[index].customElem?.desc;
|
||||
String? jsonData = msgList[index].customElem?.data;
|
||||
logger.e(msgList[index].toJson());
|
||||
jsonData = (jsonData == null || jsonData.isEmpty) ? '{"faceUrl":"","nickName":"data为空","userID":"213213"}' : jsonData;
|
||||
var item = jsonDecode(jsonData);
|
||||
if (item is Map<String, dynamic>) {
|
||||
item = jsonDecode(jsonData);
|
||||
} else {
|
||||
// 如果不是 Map,兜底处理
|
||||
item = {
|
||||
"faceUrl": "",
|
||||
"nickName": "数据异常",
|
||||
"userID": "",
|
||||
};
|
||||
}
|
||||
final showName = conv?.showName ?? '未知';
|
||||
// logger.w(element.toJson());
|
||||
|
||||
// ----测试数据
|
||||
// final jsonData = '{"faceUrl":"","nickName":"测试昵称","userID":"213213"}';
|
||||
// final item = jsonDecode(jsonData); // 数据
|
||||
// final desc = '您购买的巧克力已经下单成功您购买的巧克力已经下单成功您购买的巧克力已经下单成功';
|
||||
// final cloudCustomData = 'orderRefund';
|
||||
// V2TimMessage msg = V2TimMessage(elemType: 2, isRead: index > 2 ? true : false);
|
||||
// -----------
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
if (cloudCustomData == NotifyMessageTypeConstants.orderWithDraw ||
|
||||
cloudCustomData == NotifyMessageTypeConstants.orderRecharge) {
|
||||
// 去余额
|
||||
Get.put(BalanceController());
|
||||
Get.to(() => Balance());
|
||||
} else {
|
||||
// 点击去订单详情
|
||||
Get.toNamed('/order/detail', arguments: {'orderId': item['orderID']});
|
||||
}
|
||||
},
|
||||
child: Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
elevation: 2,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
// 顶部:头像 + 商品名
|
||||
Row(
|
||||
children: [
|
||||
ClipOval(
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: msg.faceUrl,
|
||||
width: 25,
|
||||
height: 25,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
showName,
|
||||
// item['name'] ?? '未知商品名称',
|
||||
// cloudCustomData == NotifyMessageTypeConstants.orderWithDraw
|
||||
// ? (conv?.showName ?? '未知')
|
||||
// : ((item['name'] as String?) ?? '未知商品名称'),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 8),
|
||||
const Divider(height: 1, thickness: 1, color: Color(0xFFE0E0E0)),
|
||||
const SizedBox(height: 4),
|
||||
|
||||
Text(
|
||||
_transLabel(cloudCustom: cloudCustomData, status: item['status']),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 2),
|
||||
|
||||
// 时间
|
||||
Text(
|
||||
Utils.formatTime(msg.timestamp ?? DateTime.now().millisecondsSinceEpoch ~/ 1000),
|
||||
style: TextStyle(
|
||||
color: Colors.grey[600],
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// 内容:描述 + 时间 + 图片
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
// 商品首图
|
||||
Visibility(
|
||||
visible: item['pic'] != null && item['pic'].toString().isNotEmpty,
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: item['pic'],
|
||||
placeholderAsset: 'assets/images/bk.jpg',
|
||||
width: 50,
|
||||
height: 50,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
// 商品描述
|
||||
Expanded(
|
||||
child: Text(
|
||||
desc ?? '未知描述',
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(color: Colors.black87, fontSize: 12.0),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 20),
|
||||
|
||||
// 未读角标
|
||||
Visibility(
|
||||
visible: !(msg.isRead ?? true),
|
||||
child: FStyle.badge(0, isdot: true),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
const Divider(height: 1, thickness: 1, color: Color(0xFFE0E0E0)),
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
const Text('查看详情'),
|
||||
const Spacer(),
|
||||
const Icon(Icons.arrow_forward_ios, size: 16, color: Colors.grey),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 解析标签
|
||||
String _transLabel({required String cloudCustom, String? status}) {
|
||||
//
|
||||
var str = status == null
|
||||
? '未知状态'
|
||||
: status == '1'
|
||||
? '成功'
|
||||
: '失败';
|
||||
if (NotifyMessageTypeConstants.orderPay == cloudCustom) {
|
||||
return '下单成功';
|
||||
} else if (NotifyMessageTypeConstants.orderRecharge == cloudCustom) {
|
||||
return '充值成功';
|
||||
} else if (NotifyMessageTypeConstants.orderRefund == cloudCustom) {
|
||||
return '退款$str';
|
||||
} else if (NotifyMessageTypeConstants.orderWithDraw == cloudCustom) {
|
||||
return '提现$str';
|
||||
} else {
|
||||
return '未知action';
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,517 +1 @@
|
||||
/// 聊天首页模板
|
||||
library;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||
import 'package:loopin/IM/global_badge.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||
import 'package:loopin/api/shop_api.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/components/scan_util.dart';
|
||||
import 'package:loopin/models/conversation_type.dart';
|
||||
import 'package:loopin/models/conversation_view_model.dart';
|
||||
import 'package:loopin/styles/index.dart';
|
||||
import 'package:loopin/utils/parse_message_summary.dart';
|
||||
import 'package:loopin/utils/scan_code_type.dart'; // 导入外部枚举
|
||||
import 'package:shirne_dialog/shirne_dialog.dart';
|
||||
|
||||
// systemNotify, // 系统->通知
|
||||
// systemReport, // 系统->举报下架(视频,视频评论)
|
||||
// systemCheck, // 系统->审核结果(复审,驳回 ,通过)
|
||||
// systemPush, //系统->推广类的
|
||||
|
||||
class System extends StatefulWidget {
|
||||
const System({super.key});
|
||||
|
||||
@override
|
||||
State<System> createState() => SystemState();
|
||||
}
|
||||
|
||||
class SystemState extends State<System> {
|
||||
late final ChatController controller;
|
||||
dynamic writeOffInfo;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
controller = Get.find<ChatController>();
|
||||
}
|
||||
|
||||
void deletConv(context, ConversationViewModel item) async {
|
||||
final res = await ImService.instance.deleteConversation(conversationID: item.conversation.conversationID);
|
||||
if (res.success) {
|
||||
Navigator.of(context).pop();
|
||||
controller.chatList.remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
// 长按坐标点
|
||||
double posDX = 0.0;
|
||||
double posDY = 0.0;
|
||||
|
||||
// 下拉刷新
|
||||
Future<void> handleRefresh() async {
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
// 长按菜单
|
||||
void showContextMenu(BuildContext context, ConversationViewModel item) {
|
||||
bool isLeft = posDX > MediaQuery.of(context).size.width / 2 ? false : true;
|
||||
bool isTop = posDY > MediaQuery.of(context).size.height / 2 ? false : true;
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierColor: Colors.black12, // 遮罩透明
|
||||
builder: (context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: isTop ? posDY : posDY - 135,
|
||||
left: isLeft ? posDX : posDX - 135,
|
||||
width: 135,
|
||||
child: Material(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
|
||||
color: Colors.white,
|
||||
elevation: 2.0,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: const Text(
|
||||
'设为免打扰',
|
||||
style: TextStyle(color: Colors.black87, fontSize: 14.0),
|
||||
),
|
||||
dense: true,
|
||||
onTap: () {},
|
||||
),
|
||||
ListTile(
|
||||
title: const Text(
|
||||
'置顶消息',
|
||||
style: TextStyle(color: Colors.black87, fontSize: 14.0),
|
||||
),
|
||||
dense: true,
|
||||
onTap: () {},
|
||||
),
|
||||
ListTile(
|
||||
title: const Text(
|
||||
'不显示该消息',
|
||||
style: TextStyle(color: Colors.black87, fontSize: 14.0),
|
||||
),
|
||||
dense: true,
|
||||
onTap: () {},
|
||||
),
|
||||
ListTile(
|
||||
title: const Text(
|
||||
'删除',
|
||||
style: TextStyle(color: Colors.black87, fontSize: 14.0),
|
||||
),
|
||||
dense: true,
|
||||
onTap: () {
|
||||
deletConv(context, item);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// 处理扫码结果
|
||||
void handleScanResult(String code) {
|
||||
print('扫码结果:$code');
|
||||
|
||||
// 使用外部枚举的方法检查扫码类型
|
||||
final scanType = ScanCodeType.fromCode(code);
|
||||
if (scanType != null) {
|
||||
final value = code.substring(scanType.prefix.length + 1); // 获取后缀值
|
||||
_processScanCode(scanType, value);
|
||||
} else {
|
||||
// 未知类型的码
|
||||
Get.snackbar('未知的二维码类型', '无法识别此二维码: $code');
|
||||
}
|
||||
}
|
||||
|
||||
// 处理不同类型的扫码结果
|
||||
void _processScanCode(ScanCodeType type, String value) {
|
||||
switch (type) {
|
||||
case ScanCodeType.verification:
|
||||
_handleVerificationCode(value);
|
||||
break;
|
||||
case ScanCodeType.friend:
|
||||
_handleFriendCode(value);
|
||||
break;
|
||||
case ScanCodeType.promotion:
|
||||
_handlePromotionCode(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理核销码
|
||||
void _handleVerificationCode (String value) async {
|
||||
print('处理核销码: $value');
|
||||
// 带着核销码,跳转到商家的商品详情页面,引导商家去手动点击核销按钮
|
||||
Get.toNamed('/sellerOrder/detail', arguments: {'writeOffCodeId':value});
|
||||
}
|
||||
|
||||
// 处理好友码
|
||||
void _handleFriendCode(String value) {
|
||||
print('处理好友码: $value');
|
||||
// 模仿抖音,去个人页面手动点击关注
|
||||
Get.toNamed('/vloger', arguments: {'memberId':value});
|
||||
}
|
||||
|
||||
// 处理推广码
|
||||
void _handlePromotionCode(String value)async {
|
||||
try {
|
||||
print('处理推广码111: $value');
|
||||
final res = await Http.post('${ShopApi.bindSpreadCodeId}', data: {
|
||||
"socialCode": value
|
||||
});
|
||||
if(res != null && res['code'] == 200){
|
||||
MyDialog.toast('推广码绑定成功', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.green.withAlpha(200)));
|
||||
Get.toNamed('/vloger', arguments: {'memberId':value});
|
||||
}
|
||||
} catch (e) {
|
||||
MyDialog.toast('推广码绑定失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定推广码和人员关系
|
||||
bindSpreadCode(code) async {
|
||||
try {
|
||||
final res = await Http.post('${ShopApi.bindSpreadCodeId}', data: {
|
||||
"socialCode": "01901839908031348736"
|
||||
});
|
||||
print('绑定结果-------------->${res['res']}');
|
||||
|
||||
} catch (e) {
|
||||
|
||||
MyDialog.toast('推广码绑定失败');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.grey[50],
|
||||
appBar: AppBar(
|
||||
forceMaterialTransparency: true,
|
||||
title: Row(
|
||||
spacing: 8.0,
|
||||
children: [
|
||||
Text('消息'),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 6.0, vertical: 4.0),
|
||||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20.0), boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(20),
|
||||
offset: Offset(0.0, 1.0),
|
||||
blurRadius: 2.0,
|
||||
spreadRadius: 0.0,
|
||||
),
|
||||
]),
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
if (Get.find<GlobalBadge>().totalUnread > 0) {
|
||||
final res = await ImService.instance.clearConversationUnreadCount(conversationID: '');
|
||||
if (res.success) {
|
||||
MyDialog.toast('操作成功', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.green.withAlpha(200)));
|
||||
} else {
|
||||
MyDialog.toast(res.desc, icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
spacing: 3.0,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.cleaning_services_sharp,
|
||||
size: 14.0,
|
||||
),
|
||||
Text(
|
||||
'清除未读',
|
||||
style: TextStyle(fontSize: 12.0),
|
||||
)
|
||||
],
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
/// 先不做搜索功能了后面再说
|
||||
// IconButton(
|
||||
// icon: const Icon(Icons.search),
|
||||
// onPressed: () {},
|
||||
// ),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add_circle_outline),
|
||||
onPressed: () async {
|
||||
final paddingTop = MediaQuery.of(Get.context!).padding.top;
|
||||
|
||||
final selected = await showMenu(
|
||||
context: Get.context!,
|
||||
position: RelativeRect.fromLTRB(
|
||||
double.infinity,
|
||||
kToolbarHeight + paddingTop - 12,
|
||||
8,
|
||||
double.infinity,
|
||||
),
|
||||
color: FStyle.primaryColor,
|
||||
elevation: 8,
|
||||
items: [
|
||||
PopupMenuItem<String>(
|
||||
value: 'group',
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.group, color: Colors.white, size: 18),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
'发起群聊',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem<String>(
|
||||
value: 'friend',
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.person_add, color: Colors.white, size: 18),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
'添加朋友',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem<String>(
|
||||
value: 'scan',
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.qr_code_scanner, color: Colors.white, size: 18),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
'扫一扫',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
if (selected != null) {
|
||||
switch (selected) {
|
||||
case 'group':
|
||||
print('点击了发起群聊');
|
||||
break;
|
||||
case 'friend':
|
||||
print('点击了添加朋友');
|
||||
break;
|
||||
case 'scan':
|
||||
print('点击了扫一扫');
|
||||
ScanUtil.openScanner(onResult: handleScanResult);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ScrollConfiguration(
|
||||
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
||||
padding: EdgeInsets.all(10.0),
|
||||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(15.0), boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(10),
|
||||
offset: Offset(0.0, 1.0),
|
||||
blurRadius: 2.0,
|
||||
spreadRadius: 0.0,
|
||||
),
|
||||
]),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
'assets/images/svg/order.svg',
|
||||
height: 36.0,
|
||||
width: 36.0,
|
||||
),
|
||||
Text('群聊'),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
'assets/images/svg/kefu.svg',
|
||||
height: 36.0,
|
||||
width: 36.0,
|
||||
),
|
||||
Text('互关'),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
'assets/images/svg/comment.svg',
|
||||
height: 36.0,
|
||||
width: 36.0,
|
||||
),
|
||||
Text('粉丝'),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
'assets/images/svg/comment.svg',
|
||||
height: 36.0,
|
||||
width: 36.0,
|
||||
),
|
||||
Text('关注'),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: RefreshIndicator(
|
||||
backgroundColor: Colors.white,
|
||||
color: Color(0xFFFF5000),
|
||||
displacement: 10.0,
|
||||
onRefresh: handleRefresh,
|
||||
child: Obx(() {
|
||||
final chatList = controller.chatList;
|
||||
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: BouncingScrollPhysics(),
|
||||
itemCount: chatList.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Ink(
|
||||
// color: chatList[index]['topMost'] == null ? Colors.white : Colors.grey[100], //置顶颜色
|
||||
child: InkWell(
|
||||
splashColor: Colors.grey[200],
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
||||
child: Row(
|
||||
spacing: 10.0,
|
||||
children: <Widget>[
|
||||
// 头图
|
||||
ClipOval(
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: chatList[index].faceUrl,
|
||||
width: 50,
|
||||
height: 50,
|
||||
),
|
||||
),
|
||||
|
||||
// 消息
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
chatList[index].conversation.showName ?? '未知',
|
||||
style: TextStyle(
|
||||
fontSize: (chatList[index].conversation.conversationGroupList!.contains(ConversationType.noFriend.name)) ||
|
||||
chatList[index].isCustomAdmin != '0'
|
||||
? 20
|
||||
: 16,
|
||||
fontWeight: (chatList[index].conversation.conversationGroupList!.contains(ConversationType.noFriend.name)) ||
|
||||
chatList[index].isCustomAdmin != '0'
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal),
|
||||
),
|
||||
const SizedBox(height: 2.0),
|
||||
Text(
|
||||
chatList[index].conversation.lastMessage != null
|
||||
? parseMessageSummary(chatList[index].conversation.lastMessage!)
|
||||
: '',
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 13.0),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// 右侧
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: <Widget>[
|
||||
Visibility(
|
||||
visible: !(chatList[index].isCustomAdmin != '0' ||
|
||||
chatList[index].conversation.conversationGroupList!.contains(ConversationType.noFriend.name)),
|
||||
child: Text(
|
||||
// 转成日期字符串显示
|
||||
DateTime.fromMillisecondsSinceEpoch(
|
||||
(chatList[index].conversation.lastMessage!.timestamp ?? 0) * 1000,
|
||||
).toLocal().toString().substring(0, 16), // 简单截取年月日时分
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5.0),
|
||||
// 数字角标
|
||||
Visibility(
|
||||
visible: (chatList[index].conversation.unreadCount ?? 0) > 0,
|
||||
child: FStyle.badge(chatList[index].conversation.unreadCount ?? 0),
|
||||
),
|
||||
],
|
||||
),
|
||||
Visibility(
|
||||
visible: chatList[index].isCustomAdmin != '0' ||
|
||||
chatList[index].conversation.conversationGroupList!.contains(ConversationType.noFriend.name),
|
||||
child: const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
color: Colors.blueGrey,
|
||||
size: 14.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
if (conversationTypeFromString(chatList[index].isCustomAdmin)) {
|
||||
// 跳转对应的通知消息页
|
||||
logger.e(chatList[index].isCustomAdmin);
|
||||
} else if (chatList[index].conversation.conversationGroupList!.contains(ConversationType.noFriend.name)) {
|
||||
// 跳转陌生人消息页面
|
||||
logger.e(chatList[index].conversation.conversationGroupList);
|
||||
} else {
|
||||
// 会话id查询会话详情
|
||||
Get.toNamed('/chat', arguments: chatList[index].conversation);
|
||||
}
|
||||
},
|
||||
onTapDown: (TapDownDetails details) {
|
||||
posDX = details.globalPosition.dx;
|
||||
posDY = details.globalPosition.dy;
|
||||
},
|
||||
onLongPress: () {
|
||||
showContextMenu(context, chatList[index]);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
})),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -78,8 +78,8 @@ class _GoodsState extends State<Goods> {
|
||||
shopObj = res['data']; // 注意取 data 部分
|
||||
// 初始化选中的SKU为第一个
|
||||
if (shopObj != null && shopObj['skuList'] != null && shopObj['skuList'].isNotEmpty) {
|
||||
// selectedSku = shopObj['skuList'][0];
|
||||
// 默认选中每一个分类中的第一条数据
|
||||
// selectedSku = shopObj['skuList'][0];
|
||||
// 默认选中每一个分类中的第一条数据
|
||||
dynamic attr = shopObj['productAttr'];
|
||||
List<dynamic> attrList = [];
|
||||
if (!Utils.isEmpty(attr)) {
|
||||
@ -108,57 +108,63 @@ class _GoodsState extends State<Goods> {
|
||||
Get.back();
|
||||
}
|
||||
}
|
||||
|
||||
// 根据选中的属性定位到对应的SKU
|
||||
void locateSelectedSku() {
|
||||
if (shopObj != null && shopObj['skuList'] != null) {
|
||||
for (var sku in shopObj['skuList']) {
|
||||
try {
|
||||
final spData = jsonDecode(sku['spData'] ?? '{}');
|
||||
bool match = true;
|
||||
void locateSelectedSku() {
|
||||
if (shopObj != null && shopObj['skuList'] != null) {
|
||||
for (var sku in shopObj['skuList']) {
|
||||
try {
|
||||
final spData = jsonDecode(sku['spData'] ?? '{}');
|
||||
bool match = true;
|
||||
|
||||
// 检查所有已选属性是否匹配
|
||||
selectedAttributes.forEach((key, value) {
|
||||
if (spData[key] != value) {
|
||||
match = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (match) {
|
||||
setState(() {
|
||||
selectedSku = sku;
|
||||
// 检查所有已选属性是否匹配
|
||||
selectedAttributes.forEach((key, value) {
|
||||
if (spData[key] != value) {
|
||||
match = false;
|
||||
}
|
||||
});
|
||||
print('333333333333333333');
|
||||
print(sku);
|
||||
break;
|
||||
|
||||
if (match) {
|
||||
setState(() {
|
||||
selectedSku = sku;
|
||||
});
|
||||
print('333333333333333333');
|
||||
print(sku);
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
logger.e('解析spData错误: $e');
|
||||
}
|
||||
} catch (e) {
|
||||
logger.e('解析spData错误: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理属性选择
|
||||
void handleAttributeSelect(String attrName, String optionName) {
|
||||
setState(() {
|
||||
selectedAttributes[attrName] = optionName;
|
||||
locateSelectedSku(); // 选择属性后重新定位SKU
|
||||
});
|
||||
}
|
||||
void handleAttributeSelect(String attrName, String optionName) {
|
||||
setState(() {
|
||||
selectedAttributes[attrName] = optionName;
|
||||
locateSelectedSku(); // 选择属性后重新定位SKU
|
||||
});
|
||||
}
|
||||
|
||||
///创建定点杆
|
||||
createOrder(String goodsId) async {
|
||||
var params = {
|
||||
"type": 1, // 订单类型:1->团购;2->拼团;3->秒杀;
|
||||
"distribution": 1, // 配送方式 1->到店核销;2->自提;3->配送;
|
||||
"skuItemBOList": [
|
||||
{"skuId": goodsId, "quantity": _quantity}
|
||||
{
|
||||
"skuId": goodsId,
|
||||
"quantity": _quantity,
|
||||
"shareMemberId": Get.arguments['userID'] ?? '',
|
||||
}
|
||||
]
|
||||
};
|
||||
print('下单请求参数---->$params');
|
||||
try {
|
||||
final res = await Http.post(ShopApi.createGoodsOrder, data: params);
|
||||
var resData = res['data'];
|
||||
print('1111111111111111111111111---->$res');
|
||||
logger.w(res);
|
||||
if (resData['id'].isNotEmpty) {
|
||||
return resData['id'];
|
||||
} else {
|
||||
@ -338,6 +344,7 @@ void handleAttributeSelect(String attrName, String optionName) {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// 检查属性是否被选中
|
||||
bool isAttributeSelected(String attrName, String optionName) {
|
||||
return selectedAttributes[attrName] == optionName;
|
||||
@ -545,7 +552,7 @@ void handleAttributeSelect(String attrName, String optionName) {
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
image: DecorationImage(
|
||||
image: NetworkImage(selectedSku['pic'] != null ? selectedSku['pic'] : shopObj['pic']),
|
||||
image: NetworkImage(selectedSku['pic'] ?? shopObj['pic']),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
@ -687,7 +694,7 @@ void handleAttributeSelect(String attrName, String optionName) {
|
||||
SizedBox(height: 12),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -736,31 +743,34 @@ void handleAttributeSelect(String attrName, String optionName) {
|
||||
color: Color(0xFFFFEBEB),
|
||||
borderRadius: BorderRadius.circular(30.0),
|
||||
),
|
||||
child: shopObj['canOrder'] == true?Row(
|
||||
children: [
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
color: Color(0xFFFF5000),
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
// 这里走生成预支付订单,拿到orderId
|
||||
String skuId = selectedSku != null ? selectedSku['id'] : shopObj['skuList'][0]['id'];
|
||||
String orderId = await createOrder(skuId);
|
||||
if (orderId.isNotEmpty) {
|
||||
Get.toNamed('/order/detail', arguments: {'orderId': orderId});
|
||||
} else {
|
||||
MyDialog.toast('生成订单失败', icon: const Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
'立即购买',
|
||||
style: TextStyle(color: Colors.white, fontSize: 14.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
):null,
|
||||
child: shopObj['canOrder'] == true
|
||||
? Row(
|
||||
children: [
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
color: Color(0xFFFF5000),
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
// 这里走生成预支付订单,拿到orderId
|
||||
String skuId = selectedSku != null ? selectedSku['id'] : shopObj['skuList'][0]['id'];
|
||||
logger.e(skuId);
|
||||
String orderId = await createOrder(skuId);
|
||||
if (orderId.isNotEmpty) {
|
||||
Get.toNamed('/order/detail', arguments: {'orderId': orderId});
|
||||
} else {
|
||||
MyDialog.toast('生成订单失败', icon: const Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
'立即购买',
|
||||
style: TextStyle(color: Colors.white, fontSize: 14.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -10,6 +10,7 @@ import 'package:loopin/pages/groupChat/components/invite_action_sheet.dart';
|
||||
import 'package:loopin/pages/groupChat/components/member_action_sheet.dart';
|
||||
import 'package:loopin/pages/groupChat/components/set_group_info.dart';
|
||||
import 'package:loopin/pages/groupChat/controller/group_detail_controller.dart';
|
||||
import 'package:loopin/pages/groupChat/reportGp.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/utils/index.dart';
|
||||
import 'package:loopin/utils/permissions.dart';
|
||||
@ -500,13 +501,14 @@ class GroupdetailState extends State<Groupdetail> {
|
||||
const Divider(height: 1),
|
||||
|
||||
// 举报
|
||||
// ListTile(
|
||||
// title: const Text("举报"),
|
||||
// trailing: const Icon(Icons.chevron_right),
|
||||
// onTap: () {
|
||||
// //
|
||||
// },
|
||||
// ),
|
||||
ListTile(
|
||||
title: const Text("举报"),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {
|
||||
//
|
||||
Get.to(() => ReportGp(groupID: controller.info.value!.groupID));
|
||||
},
|
||||
),
|
||||
|
||||
// 退出群聊
|
||||
const SizedBox(height: 20),
|
||||
|
||||
250
lib/pages/groupChat/reportGp.dart
Normal file
@ -0,0 +1,250 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/api/video_api.dart';
|
||||
import 'package:loopin/components/my_toast.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/utils/getCommonDictionary.dart';
|
||||
|
||||
import '../../behavior/custom_scroll_behavior.dart';
|
||||
|
||||
class ReportGp extends StatefulWidget {
|
||||
final String groupID;
|
||||
const ReportGp({super.key, required this.groupID});
|
||||
|
||||
@override
|
||||
State<ReportGp> createState() => _ReportGpState();
|
||||
}
|
||||
|
||||
class _ReportGpState extends State<ReportGp> with SingleTickerProviderStateMixin {
|
||||
int? _selectedIndex;
|
||||
late dynamic args;
|
||||
final TextEditingController _reportController = TextEditingController();
|
||||
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
|
||||
List<dynamic> reasonTypeData = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
args = Get.arguments ?? {};
|
||||
getReportReasons('ums_chat_report'); // 获取投诉枚举
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_reportController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// 加载数据的方法
|
||||
Future<void> getReportReasons(String key) async {
|
||||
try {
|
||||
final data = await Commondictionary.getCommonDictionary(key);
|
||||
setState(() {
|
||||
reasonTypeData = data;
|
||||
});
|
||||
} catch (e) {
|
||||
print('加载失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// 举报
|
||||
Future<void> doReport(String dictValue, String reasonText) async {
|
||||
try {
|
||||
final res = await Http.post(VideoApi.reportVideoApi, data: {
|
||||
'type': '1', // 类型 1 举报 2 投诉 3 建议
|
||||
'content': reasonText,
|
||||
'aimId': widget.groupID,
|
||||
'aimType': '2', // 1 会员 2 群组 3 评论 4 视频 5 聊天
|
||||
'reasonType': dictValue
|
||||
});
|
||||
if (res['code'] == 200) {
|
||||
// 投诉成功,返回视频页面
|
||||
MyToast().tip(
|
||||
title: '投诉成功',
|
||||
position: 'center',
|
||||
type: 'success',
|
||||
);
|
||||
Get.back(result: {'returnTo': '/'});
|
||||
} else {
|
||||
MyToast().tip(
|
||||
title: '投诉失败',
|
||||
position: 'center',
|
||||
type: 'error',
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
print('投诉失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
key: scaffoldKey,
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
elevation: 0,
|
||||
title: const Text(
|
||||
'举报',
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
body: ScrollConfiguration(
|
||||
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'您的举报我们将尽快受理,核实后我们将第一时间告知受理结果,请尽量提交完整的举报描述',
|
||||
style: TextStyle(fontSize: 13, color: Colors.grey, height: 1.4),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// 两列布局的投诉选项
|
||||
reasonTypeData.isEmpty
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 8,
|
||||
mainAxisSpacing: 2,
|
||||
childAspectRatio: 4,
|
||||
),
|
||||
itemCount: reasonTypeData.length,
|
||||
itemBuilder: (context, index) {
|
||||
return _buildRadioItem(index);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
const Text(
|
||||
'举报描述(选填)',
|
||||
style: TextStyle(fontSize: 14, color: Colors.black54, fontWeight: FontWeight.w500),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: TextField(
|
||||
controller: _reportController,
|
||||
maxLines: 5,
|
||||
maxLength: 32,
|
||||
cursorColor: Colors.black54,
|
||||
style: const TextStyle(
|
||||
color: Colors.black54,
|
||||
fontSize: 14,
|
||||
),
|
||||
decoration: const InputDecoration(
|
||||
contentPadding: EdgeInsets.all(12),
|
||||
border: InputBorder.none,
|
||||
hintText: '请输入内容',
|
||||
hintStyle: TextStyle(color: Colors.grey, fontSize: 13),
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text(
|
||||
'${_reportController.text.length}/32',
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: _selectedIndex != null ? _submitReport : null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
),
|
||||
child: const Text('提交', style: TextStyle(fontSize: 15)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRadioItem(int index) {
|
||||
final item = reasonTypeData[index];
|
||||
final isSelected = _selectedIndex == index;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_selectedIndex = index;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 20,
|
||||
height: 20,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: isSelected ? Colors.green : Colors.grey,
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
child: isSelected
|
||||
? Center(
|
||||
child: Container(
|
||||
width: 12,
|
||||
height: 12,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.green,
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
item['dictLabel'] ?? '未知原因',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: isSelected ? Colors.green : Colors.black54,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _submitReport() {
|
||||
if (_selectedIndex == null) {
|
||||
MyToast().tip(
|
||||
title: '请选择举报原因!',
|
||||
position: 'center',
|
||||
type: 'error',
|
||||
);
|
||||
return;
|
||||
}
|
||||
// 获取选中的原因
|
||||
final selectedReason = reasonTypeData[_selectedIndex!];
|
||||
final reasonText = _reportController.text;
|
||||
doReport(selectedReason['dictValue'], reasonText);
|
||||
}
|
||||
}
|
||||
@ -1,20 +1,26 @@
|
||||
/// 首页模板
|
||||
library;
|
||||
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:card_swiper/card_swiper.dart';
|
||||
import 'package:dynamic_tabbar/dynamic_tabbar.dart';
|
||||
import 'package:easy_refresh/easy_refresh.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/im_friend_listeners.dart';
|
||||
import 'package:loopin/components/backtop.dart';
|
||||
import 'package:loopin/api/shop_api.dart';
|
||||
import 'package:loopin/components/custom_pageview_indicator.dart';
|
||||
import 'package:loopin/components/custom_sticky_header.dart';
|
||||
import 'package:loopin/components/empty_tip.dart';
|
||||
import 'package:loopin/components/loading.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/controller/shop_index_controller.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/styles/index.dart';
|
||||
import 'package:loopin/utils/index.dart';
|
||||
|
||||
import '../../behavior/custom_scroll_behavior.dart';
|
||||
import '../../components/backtop.dart';
|
||||
|
||||
class IndexPage extends StatefulWidget {
|
||||
const IndexPage({super.key});
|
||||
|
||||
@ -22,30 +28,223 @@ class IndexPage extends StatefulWidget {
|
||||
State<IndexPage> createState() => _IndexPageState();
|
||||
}
|
||||
|
||||
class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMixin {
|
||||
class _IndexPageState extends State<IndexPage> with TickerProviderStateMixin {
|
||||
///-----------
|
||||
|
||||
// 瀑布流列表
|
||||
List waterfallData = [
|
||||
{
|
||||
'price': 199.00,
|
||||
'title': '韩料界的萨莉亚!',
|
||||
'shop': '萨莉亚专卖店',
|
||||
'image': 'https://qcloud.dpfile.com/pc/1c3egbzM_ICz90dhi6MAiTsazjxWYQcHCd-sbpD1Wqtph2eIJA04NCRvoGqL4_opG45IiB1YIyNuDTtqzVRwesm_qA1Pf8rFcayTY-n-rG8.jpg',
|
||||
'saleNum': '2.1万'
|
||||
},
|
||||
{
|
||||
'price': 1499.90,
|
||||
'title': '茅台(MOUTAI)飞天 53%vol 500ml 贵州茅台酒(带杯)',
|
||||
'shop': '茅台京东自营旗舰店',
|
||||
'image': 'https://img13.360buyimg.com/n1/jfs/t1/97097/12/15694/245806/5e7373e6Ec4d1b0ac/9d8c13728cc2544d.jpg',
|
||||
'saleNum': '1254'
|
||||
},
|
||||
{
|
||||
'price': 18.90,
|
||||
'title': '上海街头苹果糖!一口一个不吱声',
|
||||
'shop': '芝洛洛自营旗舰店',
|
||||
'image': 'https://p0.meituan.net/coverpic/f0eefdfa02619fb09ca53eacd4d97231123115.jpg',
|
||||
'saleNum': '1.2万'
|
||||
},
|
||||
{
|
||||
'price': 59.00,
|
||||
'title': '谁懂,就是这个菜,尝了第一口,立马决定加单了,真正的咸甜永动机啊🍬 去过云南的朋友都知道,当地的乳扇真的很好吃。',
|
||||
'shop': '薄荷牛舌卷旗舰店',
|
||||
'image': 'https://qcloud.dpfile.com/pc/UcW-v6AN1TxVTt9--5Kaw2-t4W55jUhEG_pM5S-w_AQ4IP3z9WxHzwJ9fOthIjEYY0q73sB2DyQcgmKUxZFQtw.jpg',
|
||||
'saleNum': '1639'
|
||||
},
|
||||
{
|
||||
'price': 2499.00,
|
||||
'title': '小米 REDMI K80 国家补贴 第三代骁龙 8 6550mAh大电池 澎湃OS 玄夜黑 12GB+256GB 红米5G至尊手机',
|
||||
'shop': '小米京东自营旗舰店',
|
||||
'image': 'https://img10.360buyimg.com/n1/s450x450_jfs/t1/264409/38/13856/102861/678dcfdaFb723c58f/5b97cf154bbba96c.jpg',
|
||||
'saleNum': '9726'
|
||||
},
|
||||
{
|
||||
'price': 1.00,
|
||||
'title': '圣菲尔伯爵法国红酒Saintfilcount干红葡萄酒珍藏13.5度单瓶送礼红酒 一元试饮',
|
||||
'shop': '小森葡萄酒专营店',
|
||||
'image': 'https://img10.360buyimg.com/n7/jfs/t1/226168/23/3411/118733/65537e5fF2db2d109/7d1d11a8013d6e8f.jpg',
|
||||
'saleNum': '9.9万'
|
||||
},
|
||||
{
|
||||
'price': 42.00,
|
||||
'title': '美的(Midea)LED便携充电小台灯书桌学习阅读灯学生宿舍卧室床头灯学习台灯',
|
||||
'shop': '美的(Midea)旗舰店',
|
||||
'image': 'https://img14.360buyimg.com/mobilecms/s360x360_jfs/t1/226233/4/10194/156936/658e8f88Fcfc9cb40/cea4a48783f11a7a.jpg',
|
||||
'saleNum': '5106'
|
||||
},
|
||||
{
|
||||
'price': 22.90,
|
||||
'title': '蒙都 羊杂500g 加热即食 京东超市肉干肉脯及礼包11.11真便宜',
|
||||
'shop': '蒙都旗舰店',
|
||||
'image': 'https://img10.360buyimg.com/n7/jfs/t1/155306/32/25324/231912/62d22fb8E4ffab855/c6001ee702fb240a.jpg',
|
||||
'saleNum': '1.6万'
|
||||
},
|
||||
{
|
||||
'price': 19.90,
|
||||
'title': '『 江西炒米粉 』本次最佳😋香就一个字话。锅气的香🔥干辣椒的焦香🌶️油的润香🐷蔬菜混合的清香🥬',
|
||||
'shop': '去月球野餐嗎',
|
||||
'image': 'https://qcloud.dpfile.com/pc/pOAOL-DQRBWfkVZIWYVoy0mMQf6_UutNlOpEpGkT_nz3b1n7ZbpikPgtXMhMsjXNY0q73sB2DyQcgmKUxZFQtw.jpg',
|
||||
'saleNum': '3.2万'
|
||||
},
|
||||
{
|
||||
'price': 109.00,
|
||||
'title': '附近新开业的,作为江西人当然要去试试。点了几个家常菜。',
|
||||
'shop': '辣评新开江西菜',
|
||||
'image': 'https://qcloud.dpfile.com/pc/HePD48CFNnS0kMZyf3Q391wxaW_zVgHimctthH__J6UI54HLPUkNt5e3qtP4Nl2G_aW_B6sGElzX-tSmYRvRnQxxxek7cKy7_R0W-KdxWUk.jpg',
|
||||
'saleNum': '8764'
|
||||
},
|
||||
];
|
||||
// 列表
|
||||
RxList dataList = [].obs;
|
||||
// 是否加载中
|
||||
RxBool isLoading = false.obs;
|
||||
RxBool isInitLoading = true.obs;
|
||||
RxBool hasMore = true.obs;
|
||||
|
||||
//
|
||||
RxInt currentIndex = 0.obs;
|
||||
TextEditingController textEditingController = TextEditingController();
|
||||
FocusNode focusNode = FocusNode();
|
||||
TabController? tabController;
|
||||
// 分类列表
|
||||
// List cateList = [
|
||||
// {
|
||||
// 'id': 1,
|
||||
// 'list': [
|
||||
// {
|
||||
// 'icon': 'order.svg',
|
||||
// 'label': '我的订单',
|
||||
// },
|
||||
// {
|
||||
// 'icon': 'chongzhi.svg',
|
||||
// 'label': '充值中心',
|
||||
// },
|
||||
// {'icon': 'qianbao.svg', 'label': '余额'},
|
||||
// {'icon': 'comment.svg', 'label': '评价中心'}
|
||||
// ]
|
||||
// }
|
||||
// ];
|
||||
final ScrollController pageScrollController = ScrollController();
|
||||
late ShopIndexController controller;
|
||||
RxList<dynamic> tabList = <dynamic>[].obs;
|
||||
|
||||
///轮播图数据
|
||||
RxList<dynamic> swiperData = <dynamic>[].obs;
|
||||
late ScrollController scrollController = ScrollController();
|
||||
final PageController pageController = PageController();
|
||||
// 滚动位置
|
||||
double scrollOffset = 0;
|
||||
int page = 1;
|
||||
|
||||
/// 初始化 Tab 分类
|
||||
Future<void> initTabs() async {
|
||||
page = 1;
|
||||
currentIndex.value = 0;
|
||||
isLoading.value = false;
|
||||
hasMore.value = true;
|
||||
dataList.value = [];
|
||||
isInitLoading.value = true;
|
||||
|
||||
// 赋值 tab 数据
|
||||
final res = await Http.post(ShopApi.shopCategory, data: {
|
||||
'level': 1,
|
||||
});
|
||||
final data = res['data'] as List<dynamic>;
|
||||
tabList.value = data;
|
||||
// 如果之前有 controller,要先释放掉
|
||||
tabController?.dispose();
|
||||
tabController = TabController(
|
||||
initialIndex: 0,
|
||||
length: tabList.length,
|
||||
vsync: this,
|
||||
);
|
||||
if (tabList.isNotEmpty) {
|
||||
loadSwiperData();
|
||||
loadData(currentIndex.value);
|
||||
}
|
||||
}
|
||||
|
||||
/// 加载swiper数据
|
||||
Future<void> loadSwiperData() async {
|
||||
final res = await Http.post(ShopApi.shopSwiperList, data: {
|
||||
'type': 1,
|
||||
});
|
||||
final data = res['data'];
|
||||
logger.w(res);
|
||||
swiperData.assignAll(data);
|
||||
}
|
||||
|
||||
/// 切换数据
|
||||
Future<void> changeData(int index) async {
|
||||
if (isLoading.value) return;
|
||||
isLoading.value = true;
|
||||
final res = await Http.post(ShopApi.shopList, data: {
|
||||
'size': 10,
|
||||
'current': page,
|
||||
'categoryId': tabList[index]['id'],
|
||||
});
|
||||
|
||||
final data = res['data']['records'];
|
||||
final total = res['data']['total'];
|
||||
logger.w(res);
|
||||
dataList.value = data;
|
||||
if (dataList.length >= total) {
|
||||
hasMore.value = false;
|
||||
}
|
||||
|
||||
// logger.w(res);
|
||||
page += 1;
|
||||
isLoading.value = false;
|
||||
isInitLoading.value = false;
|
||||
}
|
||||
|
||||
/// 加载pageview数据
|
||||
Future<void> loadData(int index) async {
|
||||
if (isLoading.value) return;
|
||||
isLoading.value = true;
|
||||
final res = await Http.post(ShopApi.shopList, data: {
|
||||
'size': 10,
|
||||
'current': page,
|
||||
'categoryId': tabList[index]['id'],
|
||||
});
|
||||
|
||||
final data = res['data']['records'];
|
||||
final total = res['data']['total'];
|
||||
logger.w(res);
|
||||
dataList.addAll(data);
|
||||
if (dataList.length >= total) {
|
||||
hasMore.value = false;
|
||||
}
|
||||
|
||||
// logger.w(res);
|
||||
page += 1;
|
||||
isLoading.value = false;
|
||||
isInitLoading.value = false;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
scrollController.addListener(() {
|
||||
setState(() {
|
||||
scrollOffset = scrollController.offset;
|
||||
});
|
||||
if (scrollController.position.pixels == scrollController.position.maxScrollExtent) {
|
||||
debugPrint('[index]滚动到底部');
|
||||
if (!isLoading.value) {
|
||||
loadData(currentIndex.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
// 初始化加载
|
||||
initTabs();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
scrollController.dispose();
|
||||
pageController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// 瀑布流卡片
|
||||
Widget cardList(item) {
|
||||
if (item == null) {
|
||||
return Loading(
|
||||
title: '加载中',
|
||||
);
|
||||
}
|
||||
return GestureDetector(
|
||||
child: Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
@ -59,11 +258,10 @@ class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMix
|
||||
]),
|
||||
child: Column(
|
||||
children: [
|
||||
// Image.network(),
|
||||
NetworkOrAssetImage(
|
||||
imageUrl: '${item['pic']}',
|
||||
width: double.infinity,
|
||||
placeholderAsset: 'assets/images/bk.jpg',
|
||||
placeholderAsset: 'assets/images/wait_loading.png',
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
|
||||
@ -113,218 +311,265 @@ class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMix
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
controller = Get.find<ShopIndexController>();
|
||||
// controller.initTabs(vsync: this);
|
||||
controller.initTabs();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.grey[50],
|
||||
body: Column(
|
||||
children: [
|
||||
_buildTopSection(),
|
||||
// 内容区域
|
||||
Expanded(
|
||||
child: controller.tabList.isEmpty
|
||||
? Center(child: CircularProgressIndicator())
|
||||
: Obx(
|
||||
() {
|
||||
final tabs = controller.tabList.asMap().entries.map((entry) {
|
||||
final idx = entry.key;
|
||||
final item = entry.value;
|
||||
return TabData(
|
||||
index: idx,
|
||||
title: Tab(
|
||||
child: Center(
|
||||
child: Text(
|
||||
item['name'] ?? '',
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: false,
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
focusNode.unfocus();
|
||||
},
|
||||
child: Scaffold(
|
||||
body: ScrollConfiguration(
|
||||
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||
child: RefreshIndicator(
|
||||
color: FStyle.primaryColor,
|
||||
onRefresh: () async {
|
||||
await initTabs();
|
||||
},
|
||||
child: CustomScrollView(
|
||||
scrollBehavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||
controller: scrollController,
|
||||
slivers: [
|
||||
SliverAppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
foregroundColor: Colors.white,
|
||||
pinned: true,
|
||||
expandedHeight: 200.0,
|
||||
titleSpacing: 10.0,
|
||||
// 搜索框(高斯模糊背景)
|
||||
title: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(30.0),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
|
||||
child: Container(
|
||||
height: 45.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withAlpha(150),
|
||||
),
|
||||
child: TextField(
|
||||
focusNode: focusNode,
|
||||
controller: textEditingController,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
hintText: "热销商品",
|
||||
hintStyle: TextStyle(fontSize: 15.0),
|
||||
prefixIcon: Icon(
|
||||
Icons.search,
|
||||
color: Colors.black38,
|
||||
size: 21.0,
|
||||
),
|
||||
suffixIcon: Container(
|
||||
padding: EdgeInsets.only(right: 15.0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 10.0,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
focusNode.unfocus();
|
||||
if (textEditingController.text.isNotEmpty) {
|
||||
// 去搜索结果页,支持带着搜索文字和搜索tab索引
|
||||
Get.toNamed(
|
||||
'/search-result',
|
||||
arguments: {'searchWords': textEditingController.text, 'tab': 1},
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text('搜索'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 0, horizontal: 10.0),
|
||||
border: OutlineInputBorder(borderSide: BorderSide.none, borderRadius: BorderRadius.circular(30.0))),
|
||||
cursorColor: Colors.black,
|
||||
onSubmitted: (value) {
|
||||
focusNode.unfocus();
|
||||
if (value.isNotEmpty) {
|
||||
// 去搜索结果页,支持带着搜索文字和搜索tab索引
|
||||
Get.toNamed(
|
||||
'/search-result',
|
||||
arguments: {'searchWords': value, 'tab': 1},
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 自定义伸缩区域(轮播图)
|
||||
flexibleSpace: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [Color(0xFFFF5000), Color(0xFFfcaec4)],
|
||||
),
|
||||
),
|
||||
child: FlexibleSpaceBar(
|
||||
background: Obx(() {
|
||||
// 如果数据为空,返回一个空容器或占位图
|
||||
if (swiperData.isEmpty) return SizedBox.shrink();
|
||||
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: Swiper.children(
|
||||
pagination: SwiperPagination(
|
||||
builder: DotSwiperPaginationBuilder(
|
||||
color: Colors.white70,
|
||||
activeColor: Colors.white,
|
||||
size: 6.0,
|
||||
activeSize: 8.0,
|
||||
space: 4.0,
|
||||
),
|
||||
),
|
||||
indicatorLayout: PageIndicatorLayout.SCALE,
|
||||
children: swiperData.map<Widget>((itm) {
|
||||
return NetworkOrAssetImage(
|
||||
imageUrl: itm['images'],
|
||||
placeholderAsset: 'assets/images/bk.jpg',
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
content: _buildTabContent(idx),
|
||||
);
|
||||
}).toList();
|
||||
return DynamicTabBarWidget(
|
||||
onTabControllerUpdated: (tabController) {
|
||||
// controller.tabController = tabController;
|
||||
// controller.initTabs(changeController: tabController, vsync: this);
|
||||
controller.addTabListener(tabController);
|
||||
// 强制选中第一个
|
||||
if (tabController.index != 0) {
|
||||
tabController.animateTo(0);
|
||||
}
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 分类
|
||||
SliverPersistentHeader(
|
||||
pinned: false,
|
||||
delegate: CustomStickyHeader(
|
||||
child: PreferredSize(
|
||||
preferredSize: Size.fromHeight(110.0),
|
||||
child: Obx(
|
||||
() {
|
||||
return Container(
|
||||
margin: EdgeInsets.all(10.0),
|
||||
padding: EdgeInsets.symmetric(vertical: 10.0),
|
||||
height: 110.0,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: PageView.builder(
|
||||
controller: pageController,
|
||||
itemCount: (tabList.length / 4).ceil(),
|
||||
itemBuilder: (context, pageIndex) {
|
||||
final start = pageIndex * 4;
|
||||
final end = (start + 4) > tabList.length ? tabList.length : (start + 4);
|
||||
final pageItems = tabList.sublist(start, end);
|
||||
return GridView.builder(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.zero,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 4, // 一行
|
||||
mainAxisSpacing: 0, // 行间距
|
||||
),
|
||||
itemCount: pageItems.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final citem = pageItems[index];
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () {
|
||||
//切换index
|
||||
final globalIndex = start + index;
|
||||
logger.e(globalIndex);
|
||||
if (globalIndex != currentIndex.value) {
|
||||
currentIndex.value = globalIndex;
|
||||
page = 1;
|
||||
isLoading.value = false;
|
||||
changeData(globalIndex);
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
spacing: 3.0,
|
||||
children: [
|
||||
ClipOval(
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: citem['icon'],
|
||||
width: 30.0,
|
||||
height: 30.0,
|
||||
placeholderAsset: 'assets/images/wait_loading.png',
|
||||
),
|
||||
),
|
||||
Text(citem['name']),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
// 数量够翻页才显示
|
||||
CustomPageViewIndicator(
|
||||
controller: pageController,
|
||||
count: (tabList.length / 4).ceil(),
|
||||
color: Color(0xFFCECECE),
|
||||
activeColor: Color(0xFFFF5000),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
onTabChanged: (index) {
|
||||
logger.w("当前选中ssssstab: $index");
|
||||
},
|
||||
dynamicTabs: tabs,
|
||||
tabAlignment: TabAlignment.start,
|
||||
isScrollable: true,
|
||||
showNextIcon: false, // 显示前进图标
|
||||
showBackIcon: false, // 显示后退图标
|
||||
labelColor: FStyle.primaryColor,
|
||||
indicatorColor: FStyle.primaryColor,
|
||||
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
||||
unselectedLabelColor: Colors.black87,
|
||||
indicator: const UnderlineTabIndicator(
|
||||
borderSide: BorderSide(color: Color.fromARGB(255, 236, 108, 49), width: 2.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 瀑布流列表
|
||||
SliverToBoxAdapter(
|
||||
child: Obx(
|
||||
() {
|
||||
if (isInitLoading.value) {
|
||||
return Column(
|
||||
children: [
|
||||
RefreshProgressIndicator(
|
||||
backgroundColor: Colors.white,
|
||||
color: Color(0xFFFF5000),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
if (dataList.isEmpty) {
|
||||
return EmptyTip();
|
||||
}
|
||||
return Container(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Column(
|
||||
children: [
|
||||
MasonryGridView.count(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.zero,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 10.0,
|
||||
crossAxisSpacing: 10.0,
|
||||
itemCount: dataList.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return cardList(dataList[index]);
|
||||
},
|
||||
),
|
||||
Opacity(opacity: dataList.isNotEmpty && isLoading.value ? 1 : 0, child: Loading(title: 'loading...')),
|
||||
],
|
||||
),
|
||||
unselectedLabelStyle: const TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontFamily: 'Microsoft YaHei',
|
||||
),
|
||||
labelStyle: const TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontFamily: 'Microsoft YaHei',
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
dividerHeight: 0,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: Obx(() {
|
||||
final tabIndex = controller.currentTabIndex.value;
|
||||
final currentTab = controller.tabs[tabIndex];
|
||||
if (currentTab == null) return const SizedBox.shrink();
|
||||
|
||||
return Backtop(
|
||||
controller: currentTab.scrollController,
|
||||
offset: currentTab.scrollOffset.value,
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// 构建顶部固定区域
|
||||
Widget _buildTopSection() {
|
||||
if (controller.swiperData.isEmpty) {
|
||||
return SizedBox();
|
||||
}
|
||||
return Column(
|
||||
children: [
|
||||
// 轮播图
|
||||
Obx(() {
|
||||
if (controller.swiperData.isEmpty) return const SizedBox.shrink();
|
||||
if (controller.swiperData.length == 1) {
|
||||
final imageUrl = controller.swiperData.first['images'] ?? '';
|
||||
return Image.network(imageUrl, fit: BoxFit.fill, width: double.infinity, height: 240);
|
||||
}
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 240,
|
||||
child: Swiper(
|
||||
itemCount: controller.swiperData.length,
|
||||
autoplay: true,
|
||||
loop: true,
|
||||
pagination: const SwiperPagination(
|
||||
builder: DotSwiperPaginationBuilder(
|
||||
color: Colors.white70,
|
||||
activeColor: Colors.white,
|
||||
),
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final imageUrl = controller.swiperData[index]['images'] ?? '';
|
||||
return imageUrl.isNotEmpty ? Image.network(imageUrl, fit: BoxFit.fill) : const SizedBox.shrink();
|
||||
},
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// 构建标签页内容
|
||||
Widget _buildTabContent(int index) {
|
||||
final tabState = controller.tabs[index]!;
|
||||
|
||||
return Obx(() {
|
||||
if (tabState.dataList.isEmpty && tabState.isLoading.value) {
|
||||
return Center(
|
||||
child: RefreshProgressIndicator(
|
||||
backgroundColor: Colors.white,
|
||||
color: Color(0xFFFF5000),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 添加 下拉刷新
|
||||
return EasyRefresh(
|
||||
onRefresh: () async {
|
||||
await controller.refreshData(index);
|
||||
},
|
||||
header: ClassicHeader(
|
||||
dragText: '下拉刷新',
|
||||
armedText: '释放刷新',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: '加载完成',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
child: CustomScrollView(
|
||||
controller: tabState.scrollController,
|
||||
key: PageStorageKey('tab_$index'),
|
||||
physics: const AlwaysScrollableScrollPhysics(), // 确保可下拉
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
|
||||
sliver: tabState.dataList.isEmpty
|
||||
? SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: MediaQuery.of(context).size.height - 500, // 给个足够高度让下拉触发
|
||||
child: Center(child: _emptyTip('暂无数据')),
|
||||
),
|
||||
)
|
||||
: SliverMasonryGrid.count(
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 10.0,
|
||||
crossAxisSpacing: 10.0,
|
||||
childCount: tabState.dataList.length,
|
||||
itemBuilder: (context, idx) => cardList(tabState.dataList[idx]),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20.0),
|
||||
child: Center(
|
||||
child: tabState.isLoading.value
|
||||
? const Loading(title: 'loading...')
|
||||
: (tabState.dataList.isNotEmpty ? const Text('没有更多数据了') : const SizedBox.shrink()),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// 空状态提示
|
||||
Widget _emptyTip(String text) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 50),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Image.asset('assets/images/empty.png', width: 50, height: 50),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
text,
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 13),
|
||||
),
|
||||
],
|
||||
),
|
||||
// 返回顶部
|
||||
floatingActionButton: Backtop(controller: scrollController, offset: scrollOffset),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
331
lib/pages/index/index2.dart
Normal file
@ -0,0 +1,331 @@
|
||||
/// 首页模板
|
||||
library;
|
||||
|
||||
import 'package:card_swiper/card_swiper.dart';
|
||||
import 'package:dynamic_tabbar/dynamic_tabbar.dart';
|
||||
import 'package:easy_refresh/easy_refresh.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/im_friend_listeners.dart';
|
||||
import 'package:loopin/components/backtop.dart';
|
||||
import 'package:loopin/components/loading.dart';
|
||||
import 'package:loopin/components/network_or_asset_image.dart';
|
||||
import 'package:loopin/controller/shop_index_controller.dart';
|
||||
import 'package:loopin/styles/index.dart';
|
||||
import 'package:loopin/utils/index.dart';
|
||||
|
||||
class IndexPage extends StatefulWidget {
|
||||
const IndexPage({super.key});
|
||||
|
||||
@override
|
||||
State<IndexPage> createState() => _IndexPageState();
|
||||
}
|
||||
|
||||
class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMixin {
|
||||
// 分类列表
|
||||
// List cateList = [
|
||||
// {
|
||||
// 'id': 1,
|
||||
// 'list': [
|
||||
// {
|
||||
// 'icon': 'order.svg',
|
||||
// 'label': '我的订单',
|
||||
// },
|
||||
// {
|
||||
// 'icon': 'chongzhi.svg',
|
||||
// 'label': '充值中心',
|
||||
// },
|
||||
// {'icon': 'qianbao.svg', 'label': '余额'},
|
||||
// {'icon': 'comment.svg', 'label': '评价中心'}
|
||||
// ]
|
||||
// }
|
||||
// ];
|
||||
final ScrollController pageScrollController = ScrollController();
|
||||
late ShopIndexController controller;
|
||||
|
||||
// 瀑布流卡片
|
||||
Widget cardList(item) {
|
||||
return GestureDetector(
|
||||
child: Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(15.0), boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withAlpha(5),
|
||||
offset: Offset(0.0, 1.0),
|
||||
blurRadius: 1.0,
|
||||
spreadRadius: 0.0,
|
||||
),
|
||||
]),
|
||||
child: Column(
|
||||
children: [
|
||||
// Image.network(),
|
||||
NetworkOrAssetImage(
|
||||
imageUrl: '${item['pic']}',
|
||||
width: double.infinity,
|
||||
placeholderAsset: 'assets/images/bk.jpg',
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 5.0,
|
||||
children: [
|
||||
Text(
|
||||
'${item['name']}',
|
||||
style: TextStyle(fontSize: 14.0, height: 1.2),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Row(
|
||||
spacing: 5.0,
|
||||
children: [
|
||||
Text.rich(
|
||||
TextSpan(style: TextStyle(color: Colors.red, fontSize: 12.0, fontWeight: FontWeight.w700, fontFamily: 'Arial'), children: [
|
||||
TextSpan(text: '¥'),
|
||||
TextSpan(
|
||||
text: '${item['price']}',
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
)),
|
||||
]),
|
||||
),
|
||||
Text(
|
||||
'已售${Utils.graceNumber(int.parse(item['sales'] ?? '0'))}件',
|
||||
style: TextStyle(color: Colors.grey, fontSize: 10.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
'${item['storeName']}',
|
||||
style: TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
// Get.toNamed('/goods', arguments: item['id']);
|
||||
Get.toNamed('/goods', arguments: {'goodsId': item['id']});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
controller = Get.find<ShopIndexController>();
|
||||
// controller.initTabs(vsync: this);
|
||||
controller.initTabs();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.grey[50],
|
||||
body: Column(
|
||||
children: [
|
||||
_buildTopSection(),
|
||||
// 内容区域
|
||||
Expanded(
|
||||
child: controller.tabList.isEmpty
|
||||
? Center(child: CircularProgressIndicator())
|
||||
: Obx(
|
||||
() {
|
||||
final tabs = controller.tabList.asMap().entries.map((entry) {
|
||||
final idx = entry.key;
|
||||
final item = entry.value;
|
||||
return TabData(
|
||||
index: idx,
|
||||
title: Tab(
|
||||
child: Center(
|
||||
child: Text(
|
||||
item['name'] ?? '',
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
content: _buildTabContent(idx),
|
||||
);
|
||||
}).toList();
|
||||
return DynamicTabBarWidget(
|
||||
onTabControllerUpdated: (tabController) {
|
||||
// controller.tabController = tabController;
|
||||
// controller.initTabs(changeController: tabController, vsync: this);
|
||||
controller.addTabListener(tabController);
|
||||
// 强制选中第一个
|
||||
if (tabController.index != 0) {
|
||||
tabController.animateTo(0);
|
||||
}
|
||||
},
|
||||
onTabChanged: (index) {
|
||||
logger.w("当前选中ssssstab: $index");
|
||||
},
|
||||
dynamicTabs: tabs,
|
||||
tabAlignment: TabAlignment.start,
|
||||
isScrollable: true,
|
||||
showNextIcon: false, // 显示前进图标
|
||||
showBackIcon: false, // 显示后退图标
|
||||
labelColor: FStyle.primaryColor,
|
||||
indicatorColor: FStyle.primaryColor,
|
||||
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
||||
unselectedLabelColor: Colors.black87,
|
||||
indicator: const UnderlineTabIndicator(
|
||||
borderSide: BorderSide(color: Color.fromARGB(255, 236, 108, 49), width: 2.0),
|
||||
),
|
||||
unselectedLabelStyle: const TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontFamily: 'Microsoft YaHei',
|
||||
),
|
||||
labelStyle: const TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontFamily: 'Microsoft YaHei',
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
dividerHeight: 0,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: Obx(() {
|
||||
final tabIndex = controller.currentTabIndex.value;
|
||||
final currentTab = controller.tabs[tabIndex];
|
||||
if (currentTab == null) return const SizedBox.shrink();
|
||||
|
||||
return Backtop(
|
||||
controller: currentTab.scrollController,
|
||||
offset: currentTab.scrollOffset.value,
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// 构建顶部固定区域
|
||||
Widget _buildTopSection() {
|
||||
if (controller.swiperData.isEmpty) {
|
||||
return SizedBox();
|
||||
}
|
||||
return Column(
|
||||
children: [
|
||||
// 轮播图
|
||||
Obx(() {
|
||||
if (controller.swiperData.isEmpty) return const SizedBox.shrink();
|
||||
if (controller.swiperData.length == 1) {
|
||||
final imageUrl = controller.swiperData.first['images'] ?? '';
|
||||
return Image.network(imageUrl, fit: BoxFit.fill, width: double.infinity, height: 240);
|
||||
}
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 240,
|
||||
child: Swiper(
|
||||
itemCount: controller.swiperData.length,
|
||||
autoplay: true,
|
||||
loop: true,
|
||||
pagination: const SwiperPagination(
|
||||
builder: DotSwiperPaginationBuilder(
|
||||
color: Colors.white70,
|
||||
activeColor: Colors.white,
|
||||
),
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final imageUrl = controller.swiperData[index]['images'] ?? '';
|
||||
return imageUrl.isNotEmpty ? Image.network(imageUrl, fit: BoxFit.fill) : const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// 构建标签页内容
|
||||
Widget _buildTabContent(int index) {
|
||||
final tabState = controller.tabs[index]!;
|
||||
|
||||
return Obx(() {
|
||||
if (tabState.dataList.isEmpty && tabState.isLoading.value) {
|
||||
return Center(
|
||||
child: RefreshProgressIndicator(
|
||||
backgroundColor: Colors.white,
|
||||
color: Color(0xFFFF5000),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 添加 下拉刷新
|
||||
return EasyRefresh(
|
||||
onRefresh: () async {
|
||||
await controller.refreshData(index);
|
||||
},
|
||||
header: ClassicHeader(
|
||||
dragText: '下拉刷新',
|
||||
armedText: '释放刷新',
|
||||
readyText: '加载中...',
|
||||
processingText: '加载中...',
|
||||
processedText: '加载完成',
|
||||
messageText: '最后更新于 %T',
|
||||
),
|
||||
child: CustomScrollView(
|
||||
controller: tabState.scrollController,
|
||||
key: PageStorageKey('tab_$index'),
|
||||
physics: const AlwaysScrollableScrollPhysics(), // 确保可下拉
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
|
||||
sliver: tabState.dataList.isEmpty
|
||||
? SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: MediaQuery.of(context).size.height - 500, // 给个足够高度让下拉触发
|
||||
child: Center(child: _emptyTip('暂无数据')),
|
||||
),
|
||||
)
|
||||
: SliverMasonryGrid.count(
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 10.0,
|
||||
crossAxisSpacing: 10.0,
|
||||
childCount: tabState.dataList.length,
|
||||
itemBuilder: (context, idx) => cardList(tabState.dataList[idx]),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20.0),
|
||||
child: Center(
|
||||
child: tabState.isLoading.value
|
||||
? const Loading(title: 'loading...')
|
||||
: (tabState.dataList.isNotEmpty ? const Text('没有更多数据了') : const SizedBox.shrink()),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// 空状态提示
|
||||
Widget _emptyTip(String text) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 50),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Image.asset('assets/images/empty.png', width: 50, height: 50),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
text,
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 13),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -240,8 +240,8 @@ class _AllFunctionsPageState extends State<AllFunctionsPage> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
MyQrcode(
|
||||
prefix: QrTypeCode.tgm,
|
||||
text: isLeader ? '推广码' : '',
|
||||
prefix: isLeader ? QrTypeCode.tgm : QrTypeCode.hym,
|
||||
text: isLeader ? '团长码' : '',
|
||||
)
|
||||
],
|
||||
),
|
||||
|
||||
@ -64,7 +64,10 @@ class _DeleteState extends State<Delete> {
|
||||
MyDialog.toast('验证码不能为空', icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||
} else {
|
||||
final dialogController = MyDialog.loading('注销中...');
|
||||
// 执行退出登录逻辑,执行注销逻辑
|
||||
// 执行注销逻辑
|
||||
final del = await Http.post('${CommonApi.revoked}?smsCode=${authObj['smsCode']}');
|
||||
logger.w(del);
|
||||
// 执行退出登录逻辑
|
||||
final loginRes = await ImService.instance.logout();
|
||||
if (loginRes.success) {
|
||||
// 清除存储信息
|
||||
@ -72,11 +75,12 @@ class _DeleteState extends State<Delete> {
|
||||
// 初始化视频
|
||||
final videoController = Get.find<VideoModuleController>();
|
||||
videoController.init();
|
||||
// 执行主席到逻辑
|
||||
final del = await Http.post('${CommonApi.revoked}?smsCode=${authObj['smsCode']}');
|
||||
logger.w(del);
|
||||
//
|
||||
Get.offAllNamed('/');
|
||||
Get.offAllNamed(
|
||||
'/login',
|
||||
predicate: (route) {
|
||||
return route.settings.name == '/';
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,7 +66,8 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
//用户基本信息
|
||||
// late Rx<V2TimUserFullInfo?> userInfo = Rx<V2TimUserFullInfo?>(null);
|
||||
|
||||
ImUserInfoController? imUserInfoController;
|
||||
// ImUserInfoController? imUserInfoController;
|
||||
late ImUserInfoController imUserInfoController;
|
||||
|
||||
// 关注,互关,粉丝数量
|
||||
late Rx<V2TimFollowInfo?> followInfo = Rx<V2TimFollowInfo?>(null);
|
||||
@ -87,13 +88,14 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
|
||||
late Callback tabListener;
|
||||
late Callback scrollListener;
|
||||
late int vlogLikeCount = 0; // 点赞数量
|
||||
late RxInt vlogLikeCount = 0.obs; // 点赞数量
|
||||
|
||||
RxBool isPinned = false.obs; // 是否吸顶
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
imUserInfoController = Get.find<ImUserInfoController>();
|
||||
initControllers();
|
||||
|
||||
scrollListener = () {
|
||||
@ -140,7 +142,7 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
try {
|
||||
final resData = await Http.get(CommonApi.accountInfo);
|
||||
if (resData != null && resData['code'] == 200) {
|
||||
vlogLikeCount = resData['data']['vlogLikeCount'] ?? 0;
|
||||
vlogLikeCount.value = resData['data']['vlogLikeCount'] ?? 0;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
@ -168,7 +170,7 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
// itemsParams.isInitLoading = true;
|
||||
|
||||
final res = await Http.post(VideoApi.myPublicList, data: {
|
||||
"userId": imUserInfoController?.userID.value,
|
||||
"userId": imUserInfoController.userID.value,
|
||||
"yesOrNo": 0,
|
||||
"current": itemsParams.page,
|
||||
"size": itemsParams.pageSize,
|
||||
@ -197,7 +199,7 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
// favoriteParams.isInitLoading = true;
|
||||
|
||||
final res = await Http.post(VideoApi.myLikedList, data: {
|
||||
"userId": imUserInfoController?.userID.value,
|
||||
"userId": imUserInfoController.userID.value,
|
||||
"yesOrNo": 0,
|
||||
"current": favoriteParams.page,
|
||||
"size": favoriteParams.pageSize,
|
||||
@ -255,7 +257,7 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
logger.e('用户信息controller未注册');
|
||||
return;
|
||||
}
|
||||
final res = await ImService.instance.getUserFollowInfo(userIDList: [imUserInfoController?.userID.value ?? '']);
|
||||
final res = await ImService.instance.getUserFollowInfo(userIDList: [imUserInfoController.userID.value ?? '']);
|
||||
if (res.success) {
|
||||
//这里少个点赞,从服务端获取
|
||||
// followersCount粉丝,多少人关注了我,mutualFollowersCount互关,followingCount我关注了多少人
|
||||
@ -269,18 +271,18 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
void deletePersonalVideo(videoId) async {
|
||||
logger.i('删除视频$videoId');
|
||||
try {
|
||||
final res = await Http.post('${VideoApi.deleteVideo}?id=$videoId', data: {});
|
||||
final res = await Http.get('${VideoApi.deleteVideo}?id=$videoId');
|
||||
logger.i('删除成功响应$res');
|
||||
if (res != null && res['code'] == 200) {
|
||||
MyDialog.toast('删除成功', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.green.withAlpha(200)));
|
||||
loadData(0);
|
||||
refreshData();
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// 二维码名片弹窗
|
||||
void qrcodeAlertDialog(BuildContext context) {
|
||||
final role = imUserInfoController?.role.value ?? 0;
|
||||
final role = imUserInfoController.role.value ?? 0;
|
||||
final isLeader = Utils.hasRole(role, 5);
|
||||
showDialog(
|
||||
context: context,
|
||||
@ -402,9 +404,9 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
Obx(() => _buildStatsCard()),
|
||||
_buildStatsCard(),
|
||||
const SizedBox(height: 10),
|
||||
Obx(() => _buildInfoDesc(context)),
|
||||
_buildInfoDesc(context),
|
||||
const SizedBox(height: 10.0),
|
||||
_buildOrderCard(context),
|
||||
const SizedBox(height: 10.0),
|
||||
@ -499,8 +501,8 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
}
|
||||
|
||||
Widget _buildInfoDesc(BuildContext context) {
|
||||
final tx = imUserInfoController?.signature;
|
||||
if (tx == null || tx.isEmpty) {
|
||||
final tx = imUserInfoController.signature;
|
||||
if (tx.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Container(
|
||||
@ -520,9 +522,11 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Text(
|
||||
'${imUserInfoController!.signature}',
|
||||
style: const TextStyle(fontSize: 16),
|
||||
child: Obx(
|
||||
() => Text(
|
||||
'${imUserInfoController.signature}',
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -538,48 +542,47 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
if (listToShow.isEmpty) {
|
||||
return emptyTip('暂无相关数据');
|
||||
}
|
||||
|
||||
return Obx(() {
|
||||
return CustomScrollView(
|
||||
// physics: !isPinned.value ? NeverScrollableScrollPhysics() : AlwaysScrollableScrollPhysics(),
|
||||
// physics: AlwaysScrollableScrollPhysics(),
|
||||
key: PageStorageKey('myindex_$tabIndex'),
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
sliver: SliverGrid(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue[100 * ((index % 8) + 1)],
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: _buildVdCard(listToShow[index], tabIndex),
|
||||
);
|
||||
},
|
||||
childCount: listToShow.length,
|
||||
),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
crossAxisSpacing: 10.0,
|
||||
mainAxisSpacing: 10.0,
|
||||
childAspectRatio: 0.6,
|
||||
return CustomScrollView(
|
||||
// physics: !isPinned.value ? NeverScrollableScrollPhysics() : AlwaysScrollableScrollPhysics(),
|
||||
// physics: AlwaysScrollableScrollPhysics(),
|
||||
key: PageStorageKey('myindex_$tabIndex'),
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
sliver: SliverGrid(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue[100 * ((index % 8) + 1)],
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: _buildVdCard(listToShow[index], tabIndex),
|
||||
);
|
||||
},
|
||||
childCount: listToShow.length,
|
||||
),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
crossAxisSpacing: 10.0,
|
||||
mainAxisSpacing: 10.0,
|
||||
childAspectRatio: 0.6,
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20.0),
|
||||
child: Center(
|
||||
child: Obx(
|
||||
() => params.hasMore.value ? CircularProgressIndicator() : Text('没有更多数据了'),
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20.0),
|
||||
child: Center(
|
||||
child: params.hasMore.value ? CircularProgressIndicator() : Text('没有更多数据了'),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildVdCard(item, tabIndex) {
|
||||
@ -688,6 +691,8 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
}
|
||||
|
||||
Widget _buildFlexibleSpace() {
|
||||
logger.e('定位');
|
||||
logger.w(imUserInfoController);
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final double maxHeight = 180;
|
||||
@ -699,14 +704,17 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
// 背景图 Obx
|
||||
Obx(() {
|
||||
final bgUrl = imUserInfoController?.customInfo['coverBg'] ?? '';
|
||||
return NetworkOrAssetImage(
|
||||
imageUrl: bgUrl,
|
||||
placeholderAsset: 'assets/images/bk.jpg',
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
}),
|
||||
Obx(
|
||||
() {
|
||||
final map = imUserInfoController.customInfo ?? {};
|
||||
final bgUrl = map['coverBg'] ?? '';
|
||||
return NetworkOrAssetImage(
|
||||
imageUrl: bgUrl,
|
||||
placeholderAsset: 'assets/images/bk.jpg',
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
Positioned(
|
||||
left: 15,
|
||||
@ -719,10 +727,10 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
children: [
|
||||
// 头像 Obx
|
||||
Obx(() {
|
||||
final faceUrl = imUserInfoController?.faceUrl.value ?? '';
|
||||
final faceUrl = imUserInfoController.faceUrl.value;
|
||||
return ClipOval(
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: faceUrl,
|
||||
imageUrl: faceUrl ?? '',
|
||||
width: 80,
|
||||
height: 80,
|
||||
),
|
||||
@ -735,27 +743,24 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
// 昵称 Obx
|
||||
Obx(
|
||||
() {
|
||||
final nickname = imUserInfoController?.nickname.value ?? '';
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withAlpha(76),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Text(
|
||||
nickname.isNotEmpty ? nickname : '昵称',
|
||||
// '啊啊啊啊啊啊啊啊',
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
// 昵称
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withAlpha(76),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Obx(() {
|
||||
final nickname = imUserInfoController.nickname.value ?? '';
|
||||
return Text(
|
||||
nickname.isNotEmpty ? nickname : '昵称',
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
);
|
||||
},
|
||||
}),
|
||||
),
|
||||
|
||||
// 团长的二维码
|
||||
@ -809,15 +814,15 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// 用户ID Obx
|
||||
Obx(() {
|
||||
final userId = imUserInfoController?.userID.value ?? '';
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withAlpha(76),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: InkWell(
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withAlpha(76),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Obx(() {
|
||||
final userId = imUserInfoController.userID.value ?? '';
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: userId));
|
||||
MyDialog.toast(
|
||||
@ -827,9 +832,9 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
);
|
||||
},
|
||||
child: Text('ID:$userId', style: const TextStyle(fontSize: 12, color: Colors.white)),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -856,9 +861,10 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
// '已售${Utils.graceNumber(int.tryParse(vlogLikeCount?.toString() ?? '0') ?? 0)}',
|
||||
Column(children: [
|
||||
Text(Utils.graceNumber(vlogLikeCount), style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)),
|
||||
Obx(() {
|
||||
return Text(Utils.graceNumber(vlogLikeCount.value), style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold));
|
||||
}),
|
||||
SizedBox(height: 3.0),
|
||||
Text('获赞')
|
||||
]),
|
||||
@ -869,7 +875,7 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
refreshData();
|
||||
},
|
||||
child: Column(children: [
|
||||
Text('${followInfo.value?.mutualFollowersCount ?? 0}', style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)),
|
||||
Obx(() => Text('${followInfo.value?.mutualFollowersCount ?? 0}', style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold))),
|
||||
SizedBox(height: 3.0),
|
||||
Text('互关')
|
||||
]),
|
||||
@ -881,7 +887,7 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
refreshData();
|
||||
},
|
||||
child: Column(children: [
|
||||
Text('${followInfo.value?.followingCount ?? 0}', style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)),
|
||||
Obx(() => Text('${followInfo.value?.followingCount ?? 0}', style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold))),
|
||||
SizedBox(height: 3.0),
|
||||
Text('关注')
|
||||
]),
|
||||
@ -893,9 +899,11 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
'${followInfo.value?.followersCount ?? 0}',
|
||||
style: const TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),
|
||||
Obx(
|
||||
() => Text(
|
||||
'${followInfo.value?.followersCount ?? 0}',
|
||||
style: const TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 3.0),
|
||||
const Text('粉丝'),
|
||||
@ -908,7 +916,7 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
||||
}
|
||||
|
||||
Widget _buildOrderCard(BuildContext context) {
|
||||
final role = imUserInfoController?.role.value ?? 0;
|
||||
final role = imUserInfoController.role.value ?? 0;
|
||||
final isLeader = Utils.hasRole(role, 5);
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||
import 'package:loopin/components/empty_tip.dart';
|
||||
import 'package:loopin/components/my_toast.dart';
|
||||
import 'package:loopin/pages/my/merchant/balance/controller.dart';
|
||||
import 'package:loopin/utils/index.dart';
|
||||
|
||||
@ -61,10 +62,52 @@ class _BalanceState extends State<Balance> {
|
||||
Row(
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
onPressed: () async {
|
||||
// 加充值
|
||||
controller.recharge(money: '0.1');
|
||||
// http
|
||||
// controller.recharge(money: '0.1');
|
||||
final money = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
String input = "";
|
||||
return AlertDialog(
|
||||
title: Text("请输入充值金额"),
|
||||
content: TextField(
|
||||
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
||||
decoration: InputDecoration(
|
||||
hintText: "请输入50~200之间的金额",
|
||||
),
|
||||
onChanged: (value) => input = value,
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(), // 取消
|
||||
child: Text("取消"),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
final num? value = num.tryParse(input);
|
||||
if (value == null) {
|
||||
Navigator.of(context).pop();
|
||||
MyToast().tip(title: '请输入正确的金额');
|
||||
return;
|
||||
}
|
||||
if (value < 0 || value > 200) {
|
||||
Navigator.of(context).pop();
|
||||
MyToast().tip(title: '金额必须在 50 ~ 200 之间');
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pop(input); // 返回合法输入
|
||||
},
|
||||
child: Text("确定"),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (money != null && money.isNotEmpty) {
|
||||
controller.recharge(money: money);
|
||||
}
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.orange,
|
||||
@ -74,11 +117,63 @@ class _BalanceState extends State<Balance> {
|
||||
SizedBox(width: 20),
|
||||
//
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
onPressed: () async {
|
||||
// onWithdrawPressed();
|
||||
// 提现
|
||||
// controller.recharge(money: '0.1');
|
||||
controller.withDraw(money: '1000');
|
||||
// http
|
||||
if (controller.balance.value < 10) {
|
||||
MyToast().tip(title: '钱包余额不足');
|
||||
return;
|
||||
}
|
||||
final money = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
String input = "";
|
||||
return AlertDialog(
|
||||
title: Text("请输入提现金额"),
|
||||
content: TextField(
|
||||
maxLength: 5,
|
||||
maxLines: 1,
|
||||
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
||||
decoration: InputDecoration(
|
||||
hintText: "当前可提现金额为:${controller.balance.value}",
|
||||
),
|
||||
onChanged: (value) => input = value,
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(), // 取消
|
||||
child: Text("取消"),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
final num? value = num.tryParse(input);
|
||||
if (value == null || value < 0.1) {
|
||||
Navigator.of(context).pop();
|
||||
MyToast().tip(title: '请输入正确的金额');
|
||||
return;
|
||||
}
|
||||
if (value > controller.balance.value) {
|
||||
Navigator.of(context).pop();
|
||||
MyToast().tip(title: '您当前可提现余额为:${controller.balance.value}');
|
||||
return;
|
||||
}
|
||||
if (value > 200) {
|
||||
Navigator.of(context).pop();
|
||||
MyToast().tip(title: '提现金额上限为200');
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pop(input); // 返回合法输入
|
||||
},
|
||||
child: Text("确定"),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (money != null && money.isNotEmpty) {
|
||||
controller.withDraw(money: money);
|
||||
}
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.orange,
|
||||
@ -92,6 +187,25 @@ class _BalanceState extends State<Balance> {
|
||||
}),
|
||||
),
|
||||
|
||||
//提示内容
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: SizedBox(
|
||||
width: double.infinity, // 占满父级宽度
|
||||
child: Text(
|
||||
'提示:\n'
|
||||
'1. 余额不可用于购买商品\n'
|
||||
'2. 余额只支持提现到授权的微信账户内\n'
|
||||
'3. 提现申请通过后将于一个工作日内到账\n',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.red[700],
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 下半部分:流水记录分页列表
|
||||
Expanded(
|
||||
child: EasyRefresh.builder(
|
||||
@ -146,10 +260,10 @@ class _BalanceState extends State<Balance> {
|
||||
final tx = controller.data[index];
|
||||
|
||||
return ListTile(
|
||||
title: Text(tx.source),
|
||||
subtitle: Text(tx.createTime),
|
||||
title: Text(tx.sourceName), // 来源
|
||||
subtitle: Text(tx.createTime), // 时间
|
||||
trailing: Text(
|
||||
"变动金额:${tx.changeType == 1 ? '+' : '-'}${tx.changeAmount}${tx.id}", // 变动金额
|
||||
"变动金额:${tx.changeType == 1 ? '+' : '-'}${tx.changeAmount}", // 变动金额
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: tx.changeType == 1 ? Colors.green : Colors.red,
|
||||
@ -169,3 +283,21 @@ class _BalanceState extends State<Balance> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _handleSource(String source) {
|
||||
String res;
|
||||
if (source == '3') {
|
||||
res = '充值';
|
||||
} else if (source == '2') {
|
||||
res = '待确认';
|
||||
} else if (source == '1') {
|
||||
res = '提现';
|
||||
} else if (source == '5') {
|
||||
res = '提现退款';
|
||||
} else if (source == '4') {
|
||||
res = '充值退款';
|
||||
} else {
|
||||
res = '未知';
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -5,6 +5,8 @@ import 'package:loopin/IM/im_friend_listeners.dart';
|
||||
import 'package:loopin/api/common_api.dart';
|
||||
import 'package:loopin/pages/my/merchant/balance/model.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/styles/index.dart';
|
||||
import 'package:loopin/utils/wechat_business_view.dart';
|
||||
import 'package:loopin/utils/wxsdk.dart';
|
||||
|
||||
class BalanceController extends GetxController {
|
||||
@ -20,17 +22,39 @@ class BalanceController extends GetxController {
|
||||
/// 是否还有更多
|
||||
var hasMore = true.obs;
|
||||
|
||||
//
|
||||
final RxBool buttonLoading = false.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
getAccount();
|
||||
getData(reset: true);
|
||||
}
|
||||
|
||||
///获取用户账户信息
|
||||
Future<void> getAccount() async {
|
||||
final res = await Http.get(CommonApi.userAccount);
|
||||
logger.w(res);
|
||||
final resData = res['data']['wallet'];
|
||||
if (resData != null) {
|
||||
balance.value = double.parse(resData);
|
||||
} else {
|
||||
balance.value = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/// 充值
|
||||
Future<void> recharge({required String money}) async {
|
||||
// 获取支付参数
|
||||
final data = {"orderType": "RECHARGE", "clientType": "APP", "paymentMethod": "WECHAT", "paymentClient": "APP", "money": money};
|
||||
final res = await Http.post(CommonApi.addBalance, data: data);
|
||||
final data = {
|
||||
"orderType": "RECHARGE",
|
||||
"clientType": "APP",
|
||||
"paymentMethod": "WECHAT",
|
||||
"paymentClient": "APP",
|
||||
"money": money,
|
||||
};
|
||||
final res = await Http.post(CommonApi.wechatPay, data: data);
|
||||
logger.w(res);
|
||||
final payParams = res['data'];
|
||||
logger.w(payParams);
|
||||
@ -79,30 +103,91 @@ class BalanceController extends GetxController {
|
||||
);
|
||||
return;
|
||||
}
|
||||
//
|
||||
// final res = await Http.post(CommonApi.withdraw, data: {
|
||||
// "money": money,
|
||||
// "method": "1",
|
||||
// });
|
||||
// MyDialog.('提现成功,系统将在一个工作日内将余额提现至您绑定的微信内');
|
||||
|
||||
showDialog(
|
||||
context: Get.context!,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text('提现成功'),
|
||||
content: const Text('系统将在一个工作日内将余额提现至您绑定的微信内', style: TextStyle(fontSize: 16.0)),
|
||||
title: Text('提示!'),
|
||||
content: const Text('发起提现申请后,请及时进行确认收款,未进行确认收款金额将于24小后退回', style: TextStyle(fontSize: 16.0)),
|
||||
backgroundColor: Colors.white,
|
||||
surfaceTintColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
|
||||
elevation: 2.0,
|
||||
actionsPadding: const EdgeInsets.all(15.0),
|
||||
actions: [
|
||||
TextButton(onPressed: () => {Get.back()}, child: Text('确认')),
|
||||
Obx(() {
|
||||
return TextButton(
|
||||
onPressed: buttonLoading.value
|
||||
? null
|
||||
: () async {
|
||||
buttonLoading.value = true;
|
||||
try {
|
||||
final res = await Http.post(CommonApi.withdraw, data: {
|
||||
"money": money,
|
||||
"method": "1",
|
||||
});
|
||||
logger.w(res);
|
||||
final pkg = res['data']['packageInfo'];
|
||||
await getData(reset: true);
|
||||
await getAccount();
|
||||
|
||||
await onWithdrawPressed(packageInfo: pkg);
|
||||
Get.back();
|
||||
} catch (e) {
|
||||
logger.e("提现失败$e");
|
||||
} finally {
|
||||
buttonLoading.value = false;
|
||||
}
|
||||
},
|
||||
child: buttonLoading.value
|
||||
? const SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(FStyle.primaryColor),
|
||||
),
|
||||
)
|
||||
: const Text('确认'),
|
||||
);
|
||||
}),
|
||||
// TextButton(
|
||||
// onPressed: () async {
|
||||
// //去提现
|
||||
// final res = await Http.post(CommonApi.withdraw, data: {
|
||||
// "money": money,
|
||||
// "method": "1",
|
||||
// });
|
||||
// logger.w(res);
|
||||
// final pkg = res['data']['packageInfo'];
|
||||
// await getData(reset: true);
|
||||
// await getAccount();
|
||||
|
||||
// await onWithdrawPressed(packageInfo: pkg);
|
||||
// Get.back();
|
||||
// },
|
||||
// child: Text('确认'),
|
||||
// ),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
getData(reset: true);
|
||||
}
|
||||
|
||||
Future<void> onWithdrawPressed({required String packageInfo}) async {
|
||||
String encodedPackage = Uri.encodeComponent(packageInfo);
|
||||
String query = 'mchId=1658665710&appId=wxebcdaea31881caab&package=$encodedPackage';
|
||||
logger.e(query);
|
||||
bool success = await WechatBusinessView.openBusinessView(
|
||||
packageInfo: query,
|
||||
);
|
||||
|
||||
if (success) {
|
||||
logger.w("已唤起微信确认收款码");
|
||||
} else {
|
||||
logger.e("唤起失败");
|
||||
}
|
||||
}
|
||||
|
||||
/// 分页数据
|
||||
@ -111,12 +196,16 @@ class BalanceController extends GetxController {
|
||||
logger.w('正在加载中,跳过');
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading.value = true;
|
||||
logger.w('开始加载数据,reset: $reset, currentPage: $currentPage');
|
||||
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
final res = await Http.post(CommonApi.bills, data: {
|
||||
"size": 10,
|
||||
"current": currentPage,
|
||||
});
|
||||
logger.e(res);
|
||||
final List resData = res['data']?['records'] ?? [];
|
||||
final bills = resData.map((item) => AccountBill.fromJson(item)).toList();
|
||||
|
||||
final total = res['data']['total'] ?? 0;
|
||||
if (reset) {
|
||||
logger.w('重置数据');
|
||||
currentPage = 1;
|
||||
@ -124,32 +213,16 @@ class BalanceController extends GetxController {
|
||||
hasMore.value = true;
|
||||
}
|
||||
|
||||
List<AccountBill> newData = List.generate(
|
||||
10,
|
||||
(index) {
|
||||
int id = currentPage * 10 + index + 1;
|
||||
// logger.w('生成数据: id=$id');
|
||||
return AccountBill(
|
||||
id: id,
|
||||
source: '来源 $currentPage-${index + 1}',
|
||||
changeAmount: (index + 1) * 10.0,
|
||||
changeType: index % 2 + 1,
|
||||
createTime: DateTime.now().toString(),
|
||||
);
|
||||
},
|
||||
);
|
||||
data.addAll(bills);
|
||||
|
||||
data.addAll(newData);
|
||||
logger.w('添加了 ${newData.length} 条数据,总数据量: ${data.length}');
|
||||
|
||||
currentPage++;
|
||||
logger.w('页码增加到: $currentPage');
|
||||
|
||||
if (currentPage > 5) {
|
||||
if (data.length >= total) {
|
||||
hasMore.value = false;
|
||||
logger.w('没有更多数据了');
|
||||
}
|
||||
|
||||
currentPage++;
|
||||
logger.w('页码增加到: $currentPage');
|
||||
|
||||
isLoading.value = false;
|
||||
logger.w('加载完成');
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ class AccountBill {
|
||||
final int changeType; // 变动类型(1收入 2支出)
|
||||
final String changeDesc; // 变动描述
|
||||
final String source; // 变动来源
|
||||
final String sourceName; // 变动来源文案
|
||||
final String createTime; // 创建时间
|
||||
final int createBy;
|
||||
final String updateTime;
|
||||
@ -24,6 +25,7 @@ class AccountBill {
|
||||
this.changeType = 1,
|
||||
this.changeDesc = '',
|
||||
this.source = '未知来源',
|
||||
this.sourceName = '未知来源',
|
||||
this.createTime = '',
|
||||
this.createBy = 0,
|
||||
this.updateTime = '',
|
||||
@ -31,19 +33,20 @@ class AccountBill {
|
||||
|
||||
factory AccountBill.fromJson(Map<String, dynamic> json) {
|
||||
return AccountBill(
|
||||
id: json['id'] ?? 0,
|
||||
memberId: json['member_id'],
|
||||
accountId: json['account_id'],
|
||||
moneyBalance: (json['money_balance'] as num?)?.toDouble() ?? 0.0,
|
||||
beforeBalance: (json['before_balance'] as num?)?.toDouble() ?? 0.0,
|
||||
afterBalance: (json['after_balance'] as num?)?.toDouble() ?? 0.0,
|
||||
changeAmount: (json['change_amount'] as num?)?.toDouble() ?? 0.0,
|
||||
changeType: json['change_type'] ?? 1,
|
||||
changeDesc: json['change_desc'] ?? '',
|
||||
source: json['source'] ?? '未知来源',
|
||||
createTime: json['create_time'] ?? '',
|
||||
createBy: json['create_by'] ?? 0,
|
||||
updateTime: json['update_time'] ?? '',
|
||||
id: int.tryParse(json['id']?.toString() ?? '') ?? 0,
|
||||
memberId: json['memberId'] != null ? int.tryParse(json['memberId'].toString()) : null,
|
||||
accountId: json['accountId'] != null ? int.tryParse(json['accountId'].toString()) : null,
|
||||
moneyBalance: double.tryParse(json['moneyBalance']?.toString() ?? '0') ?? 0.0,
|
||||
beforeBalance: double.tryParse(json['beforeBalance']?.toString() ?? '0') ?? 0.0,
|
||||
afterBalance: double.tryParse(json['afterBalance']?.toString() ?? '0') ?? 0.0,
|
||||
changeAmount: double.tryParse(json['changeAmount']?.toString() ?? '0') ?? 0.0,
|
||||
changeType: int.tryParse(json['changeType']?.toString() ?? '1') ?? 1,
|
||||
changeDesc: json['changeDesc'] ?? '',
|
||||
source: json['source']?.toString() ?? '未知来源',
|
||||
sourceName: json['sourceName']?.toString() ?? '未知sourceName',
|
||||
createTime: json['createTime'] ?? '',
|
||||
createBy: int.tryParse(json['createBy']?.toString() ?? '0') ?? 0,
|
||||
updateTime: json['updateTime'] ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/components/web_page.dart';
|
||||
import 'package:loopin/styles/index.dart';
|
||||
|
||||
class Setting extends StatelessWidget {
|
||||
@ -23,14 +24,19 @@ class Setting extends StatelessWidget {
|
||||
},
|
||||
{
|
||||
'icon': Icons.lock,
|
||||
'title': '隐私',
|
||||
'onTap': () => Get.toNamed('/privacy'),
|
||||
'title': '隐私协议',
|
||||
'onTap': () => Get.to(() => SafeArea(child: WebPage(url: 'https://www.wuzhongjie.com.cn/download/yinsizhengce.html', title: '隐私协议'))),
|
||||
},
|
||||
{
|
||||
'icon': Icons.info,
|
||||
'title': '关于我们',
|
||||
'onTap': () => Get.toNamed('/about'),
|
||||
'icon': Icons.lock,
|
||||
'title': '用户协议',
|
||||
'onTap': () => Get.to(() => SafeArea(child: WebPage(url: 'https://www.wuzhongjie.com.cn/download/yonhuxieyi.html', title: '用户协议'))),
|
||||
},
|
||||
// {
|
||||
// 'icon': Icons.info,
|
||||
// 'title': '关于我们',
|
||||
// 'onTap': () => Get.toNamed('/about'),
|
||||
// },
|
||||
];
|
||||
|
||||
return Scaffold(
|
||||
|
||||
@ -16,6 +16,7 @@ import 'package:nested_scroll_view_plus/nested_scroll_view_plus.dart';
|
||||
import 'package:shirne_dialog/shirne_dialog.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_follow_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
|
||||
|
||||
class PageParams {
|
||||
@ -55,6 +56,7 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
|
||||
},
|
||||
role: 0,
|
||||
));
|
||||
String blackTxt = '拉黑';
|
||||
|
||||
late RxInt followed = 0.obs; // 是否关注
|
||||
// followersCount粉丝,多少人关注了我,mutualFollowersCount互关,followingCount我关注了多少人
|
||||
@ -113,6 +115,7 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
|
||||
|
||||
loadData(0);
|
||||
getUserLikesCount();
|
||||
getBlackList();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -175,7 +178,7 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
|
||||
final resIm = await ImService.instance.otherInfo(args['memberId']);
|
||||
if (resIm.success && resIm.data != null) {
|
||||
userInfo.value = resIm.data!;
|
||||
logger.i(userInfo.value.toLogString());
|
||||
// logger.i(userInfo.value.toLogString());
|
||||
} else {
|
||||
logger.e(resIm.desc);
|
||||
}
|
||||
@ -210,6 +213,64 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取拉黑列表
|
||||
void getBlackList() async {
|
||||
final res = await ImService.instance.blackList();
|
||||
if (res.success && res.data != null) {
|
||||
//
|
||||
final data = res.data;
|
||||
for (V2TimFriendInfo element in data ?? []) {
|
||||
logger.w(element.toJson());
|
||||
if (element.userID == args['memberId']) {
|
||||
// 在拉黑列表
|
||||
blackTxt = '取消拉黑';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 取消拉黑
|
||||
void _cancelBlack() async {
|
||||
logger.w('开始执行取消拉黑${args['memberId']}');
|
||||
final res = await ImService.instance.deleteFromBlackList(userIDList: [args['memberId'] ?? '']);
|
||||
logger.w(res.success);
|
||||
// 成功后返回并删除会话
|
||||
if (res.success) {
|
||||
blackTxt = '拉黑';
|
||||
//删除会话,退出聊天
|
||||
Get.back();
|
||||
}
|
||||
}
|
||||
|
||||
// 拉黑
|
||||
void _addBlack() async {
|
||||
logger.w('开始执行拉黑${args['memberId']}');
|
||||
final res = await ImService.instance.addToBlackList(userIDList: [args['memberId'] ?? '']);
|
||||
logger.w(res.success);
|
||||
// 成功后返回并删除会话
|
||||
if (res.success) {
|
||||
blackTxt = '取消拉黑';
|
||||
//删除会话,退出聊天
|
||||
await ImService.instance.deleteConversation(conversationID: 'c2c_${args['memberId']}');
|
||||
final ctl = Get.find<ChatController>();
|
||||
ctl.chatList.removeWhere(
|
||||
(conv) => conv.conversation.conversationID == 'c2c_${args['memberId']}',
|
||||
);
|
||||
ctl.chatList.refresh();
|
||||
Get.back();
|
||||
}
|
||||
}
|
||||
|
||||
//拉黑/取消
|
||||
void _handleBlack() {
|
||||
if (blackTxt == '拉黑') {
|
||||
//_addBlack
|
||||
_addBlack();
|
||||
} else {
|
||||
_cancelBlack();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopScope(
|
||||
@ -235,6 +296,77 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
|
||||
collapsedHeight: 120.0,
|
||||
pinned: true,
|
||||
stretch: true,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.more_horiz,
|
||||
color: Colors.black,
|
||||
),
|
||||
onPressed: () async {
|
||||
final paddingTop = MediaQuery.of(Get.context!).padding.top;
|
||||
|
||||
final selected = await showMenu(
|
||||
context: Get.context!,
|
||||
position: RelativeRect.fromLTRB(
|
||||
double.infinity,
|
||||
kToolbarHeight + paddingTop - 12,
|
||||
8,
|
||||
double.infinity,
|
||||
),
|
||||
color: FStyle.primaryColor,
|
||||
elevation: 8,
|
||||
items: [
|
||||
PopupMenuItem<String>(
|
||||
value: 'block',
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.block, color: Colors.white, size: 18),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
blackTxt,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
if (selected != null) {
|
||||
switch (selected) {
|
||||
case 'block':
|
||||
// print('点击了拉黑');
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
content: Text('确认要$blackTxt对方吗?', style: TextStyle(fontSize: 16.0)),
|
||||
backgroundColor: Colors.white,
|
||||
surfaceTintColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
|
||||
elevation: 2.0,
|
||||
actionsPadding: const EdgeInsets.all(15.0),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
child: const Text('取消', style: TextStyle(color: Colors.black54)),
|
||||
),
|
||||
TextButton(onPressed: _handleBlack, child: const Text('确认', style: TextStyle(color: Colors.red))),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
break;
|
||||
// case 'foucs':
|
||||
// print('点击了取关');
|
||||
// break;
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
onPressed: () {
|
||||
|
||||
@ -1,14 +1,19 @@
|
||||
library;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||
import 'package:loopin/IM/im_friend_listeners.dart';
|
||||
import 'package:loopin/api/common_api.dart';
|
||||
import 'package:loopin/api/shop_api.dart';
|
||||
import 'package:loopin/components/my_toast.dart';
|
||||
import 'package:shirne_dialog/shirne_dialog.dart';
|
||||
import 'package:timer_count_down/timer_count_down.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/utils/wxsdk.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
import 'package:shirne_dialog/shirne_dialog.dart';
|
||||
import 'package:timer_count_down/timer_count_down.dart';
|
||||
|
||||
import '../../behavior/custom_scroll_behavior.dart';
|
||||
import '../../utils/lifecycle_handler.dart';
|
||||
|
||||
@ -20,9 +25,11 @@ class OrderDetail extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStateMixin {
|
||||
late List orderStatusDict;
|
||||
|
||||
final String _orderId = Get.arguments['orderId'] ?? '';
|
||||
dynamic orderGoodsInfo;
|
||||
int _initialSeconds = 30 * 60; // 存储初始秒数
|
||||
final int _initialSeconds = 30 * 60; // 存储初始秒数
|
||||
bool _countdownFinished = false; // 新增标志位,用于跟踪倒计时是否结束
|
||||
bool _isLoading = true; // 添加加载状态
|
||||
bool _showQrCodeDialog = false;
|
||||
@ -31,7 +38,8 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
getOrderDetail(orderId: _orderId);
|
||||
getOrderStatusDict();
|
||||
// getOrderDetail(orderId: _orderId);
|
||||
LifecycleHandler.onAppResumed = _onAppResumed;
|
||||
}
|
||||
|
||||
@ -42,7 +50,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
}
|
||||
|
||||
void _onAppResumed() {
|
||||
print('App回到前台,刷新订单状态,订单Id${_orderId}');
|
||||
print('App回到前台,刷新订单状态,订单Id$_orderId');
|
||||
getOrderDetail(orderId: _orderId); // 刷新订单详情数据
|
||||
_showPaymentResultDialog(); // 展示支付结果弹框
|
||||
}
|
||||
@ -53,7 +61,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
final res = await Http.get('${ShopApi.goodsOrderStatus}/$orderId');
|
||||
Get.toNamed('/myOrder');
|
||||
} catch (e) {
|
||||
print('报错-------------->${e}');
|
||||
print('报错-------------->$e');
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,7 +72,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
_isLoading = true;
|
||||
});
|
||||
final res = await Http.get('${ShopApi.goodsOrderDetail}/$orderId');
|
||||
debugPrint(res['data'].toString(), wrapWidth: 600);
|
||||
logger.w(res);
|
||||
setState(() {
|
||||
orderGoodsInfo = res['data'];
|
||||
_isLoading = false;
|
||||
@ -77,6 +85,12 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getOrderStatusDict() async {
|
||||
final res = await Http.get('${CommonApi.dictionaryApi}oms_order_status');
|
||||
orderStatusDict = res['data'] as List;
|
||||
getOrderDetail(orderId: _orderId);
|
||||
}
|
||||
|
||||
// 取消订单
|
||||
void _cancelOrder() async {
|
||||
try {
|
||||
@ -89,9 +103,11 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
|
||||
// 核销
|
||||
void _writeOffQrCode(verificationCodes) async {
|
||||
if (verificationCodes != null && verificationCodes.isNotEmpty) {
|
||||
logger.e(orderGoodsInfo);
|
||||
final orderInfo = orderGoodsInfo['items'];
|
||||
if (orderInfo != null) {
|
||||
// 过滤可用的核销码(status为0)
|
||||
List<dynamic> newVerifyList = verificationCodes.where((item) => item['status'] == 0).toList();
|
||||
List<dynamic> newVerifyList = orderInfo.where((item) => item['status'] == 0).toList();
|
||||
|
||||
if (newVerifyList.isNotEmpty) {
|
||||
setState(() {
|
||||
@ -108,7 +124,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
} else {
|
||||
MyToast().tip(
|
||||
title: '暂无可用的核销码',
|
||||
position: 'center',
|
||||
position: 'top',
|
||||
type: 'error',
|
||||
);
|
||||
}
|
||||
@ -229,8 +245,19 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
);
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
String getOrderStatusText(dynamic status) {
|
||||
int statusCode = status is String ? int.tryParse(status) ?? -1 : (status is int ? status : -1);
|
||||
final match = orderStatusDict.firstWhere(
|
||||
(item) => item['dictValue'].toString() == statusCode.toString(),
|
||||
orElse: () => {'dictLabel': '未知状态'},
|
||||
);
|
||||
|
||||
return match['dictLabel'] ?? '未知状态';
|
||||
}
|
||||
|
||||
// 获取订单状态文本
|
||||
String getOrderStatusText(int status) {
|
||||
String getOrderStatusText_old(int status) {
|
||||
print('111111111111$status');
|
||||
switch (status) {
|
||||
case 0:
|
||||
@ -276,10 +303,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
|
||||
// 获取第一个商品信息
|
||||
dynamic _getFirstProductInfo() {
|
||||
if (orderGoodsInfo == null ||
|
||||
orderGoodsInfo['items'] == null ||
|
||||
orderGoodsInfo['items'] is! List ||
|
||||
orderGoodsInfo['items'].isEmpty) {
|
||||
if (orderGoodsInfo == null || orderGoodsInfo['items'] == null || orderGoodsInfo['items'] is! List || orderGoodsInfo['items'].isEmpty) {
|
||||
return {};
|
||||
}
|
||||
return orderGoodsInfo['items'][0];
|
||||
@ -288,6 +312,8 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
// 构建商品图片
|
||||
Widget _buildProductImage() {
|
||||
final productInfo = _getFirstProductInfo();
|
||||
logger.e(orderGoodsInfo);
|
||||
|
||||
final picUrl = productInfo?['pic'];
|
||||
|
||||
if (picUrl == null || picUrl.isEmpty) {
|
||||
@ -337,9 +363,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(
|
||||
value: loadingProgress.expectedTotalBytes != null
|
||||
? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes!
|
||||
: null,
|
||||
value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null,
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -372,9 +396,63 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
),
|
||||
const SizedBox(width: 10.0),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
onPressed: () async {
|
||||
// 打开微信小程序的某个页面地址,如pages/index/index
|
||||
Wxsdk.openMiniApp(orderId: _orderId);
|
||||
// Wxsdk.openMiniApp(orderId: _orderId);
|
||||
|
||||
//-----------
|
||||
final infoCtl = Get.find<ImUserInfoController>();
|
||||
final openId = infoCtl.customInfo['openId'];
|
||||
if (openId == null || openId.isEmpty) {
|
||||
showDialog(
|
||||
context: Get.context!,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text('微信授权'),
|
||||
content: const Text('当前支付需要您进行微信授权', style: TextStyle(fontSize: 16.0)),
|
||||
backgroundColor: Colors.white,
|
||||
surfaceTintColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
|
||||
elevation: 2.0,
|
||||
actionsPadding: const EdgeInsets.all(15.0),
|
||||
actions: [
|
||||
TextButton(onPressed: () => {Get.back()}, child: Text('取消', style: TextStyle(color: Colors.red))),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
//去授权
|
||||
await Wxsdk.login();
|
||||
Get.back();
|
||||
},
|
||||
child: Text('去授权')),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取支付参数
|
||||
final data = {
|
||||
"orderType": "ORDER",
|
||||
"clientType": "APP",
|
||||
"paymentMethod": "WECHAT",
|
||||
"paymentClient": "APP",
|
||||
"sn": _orderId,
|
||||
};
|
||||
final res = await Http.post(CommonApi.wechatPay, data: data);
|
||||
logger.w(res);
|
||||
final payParams = res['data'];
|
||||
logger.w(payParams);
|
||||
// 拉起支付
|
||||
await Wxsdk.payWithWx(
|
||||
appId: payParams['appid'],
|
||||
partnerId: payParams['partnerid'],
|
||||
prepayId: payParams['prepayid'],
|
||||
packageValue: payParams['package'],
|
||||
nonceStr: payParams['noncestr'],
|
||||
timestamp: int.parse(payParams['timestamp']),
|
||||
sign: payParams['sign'],
|
||||
);
|
||||
},
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all(Color(0xff07c160)),
|
||||
@ -385,7 +463,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
],
|
||||
);
|
||||
|
||||
case 1: // 待核销
|
||||
case 2: // 待核销
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
@ -401,21 +479,21 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
],
|
||||
);
|
||||
|
||||
case 2: // 已完成
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
const SizedBox(width: 10.0),
|
||||
ElevatedButton(
|
||||
onPressed: () {},
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all(Colors.green),
|
||||
foregroundColor: WidgetStateProperty.all(Colors.white),
|
||||
),
|
||||
child: const Text('已完成'),
|
||||
),
|
||||
],
|
||||
);
|
||||
// case 2: // 已完成
|
||||
// return Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.end,
|
||||
// children: [
|
||||
// const SizedBox(width: 10.0),
|
||||
// ElevatedButton(
|
||||
// onPressed: () {},
|
||||
// style: ButtonStyle(
|
||||
// backgroundColor: WidgetStateProperty.all(Colors.green),
|
||||
// foregroundColor: WidgetStateProperty.all(Colors.white),
|
||||
// ),
|
||||
// child: const Text('已完成'),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
|
||||
case 3: // 已关闭
|
||||
return Row(
|
||||
@ -701,6 +779,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
build: (_, double time) {
|
||||
int m = ((time % 3600) ~/ 60).toInt();
|
||||
int s = (time % 60).toInt();
|
||||
|
||||
String formatted = "${m.toString().padLeft(2, '0')}:${s.toString().padLeft(2, '0')}";
|
||||
|
||||
return Text(
|
||||
@ -721,9 +800,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Text(
|
||||
_countdownFinished
|
||||
? '订单已自动取消'
|
||||
: '超过30分钟未支付,订单将自动取消',
|
||||
_countdownFinished ? '订单已自动取消' : '超过30分钟未支付,订单将自动取消',
|
||||
style: TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||
),
|
||||
],
|
||||
@ -750,13 +827,21 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
Row(
|
||||
children: [
|
||||
Spacer(),
|
||||
Text(
|
||||
getOrderStatusText(orderGoodsInfo?['status']),
|
||||
style: TextStyle(
|
||||
color: getOrderStatusColor(orderGoodsInfo?['status']),
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
)
|
||||
orderGoodsInfo['items'][0]['status'] == 1
|
||||
? Text(
|
||||
'已核销',
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
getOrderStatusText(orderGoodsInfo?['status']),
|
||||
style: TextStyle(
|
||||
color: getOrderStatusColor(orderGoodsInfo?['status']),
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
@ -842,7 +927,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
_buildOrderInfoRow('下单时间', orderGoodsInfo?['createTime'] ?? ''),
|
||||
_buildOrderInfoRow('购买数量', (productInfo['quantity'] ?? 0).toString()),
|
||||
_buildOrderInfoRow('订单金额', '¥${orderGoodsInfo?['totalAmount'] ?? '0.00'}'),
|
||||
_buildOrderInfoRow('实付金额', '¥${orderGoodsInfo?['payAmount'] ?? '0.00'}'),
|
||||
// _buildOrderInfoRow('实付金额', '¥${orderGoodsInfo?['payAmount'] ?? '0.00'}'),
|
||||
],
|
||||
)
|
||||
],
|
||||
@ -856,9 +941,21 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
if (_showQrCodeDialog) _buildQrCodeDialog(),
|
||||
],
|
||||
),
|
||||
bottomNavigationBar: orderGoodsInfo == null
|
||||
? null
|
||||
: SafeArea(
|
||||
// bottomNavigationBar: orderGoodsInfo == null
|
||||
// ? null
|
||||
// : SafeArea(
|
||||
// bottom: true,
|
||||
// minimum: const EdgeInsets.all(10),
|
||||
// child: Container(
|
||||
// height: 60.0,
|
||||
// color: Colors.white,
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
|
||||
// child: buildBottomButtons(),
|
||||
// ),
|
||||
// ),
|
||||
bottomNavigationBar: (orderGoodsInfo != null && !_showQrCodeDialog)
|
||||
? SafeArea(
|
||||
bottom: true,
|
||||
minimum: const EdgeInsets.all(10),
|
||||
child: Container(
|
||||
height: 60.0,
|
||||
@ -866,7 +963,8 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
|
||||
child: buildBottomButtons(),
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -3,10 +3,13 @@ library;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/utils/wxsdk.dart';
|
||||
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||
import 'package:loopin/IM/push_service.dart';
|
||||
import 'package:loopin/api/common_api.dart';
|
||||
import 'package:loopin/api/shop_api.dart';
|
||||
import 'package:loopin/components/my_toast.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/utils/wxsdk.dart';
|
||||
|
||||
import '../../behavior/custom_scroll_behavior.dart';
|
||||
|
||||
@ -28,36 +31,39 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
|
||||
List<Map<String, dynamic>> _allOrders = [];
|
||||
List<Map<String, dynamic>> _pendingPaymentOrders = [];
|
||||
List<Map<String, dynamic>> _pendingVerificationOrders = [];
|
||||
List<Map<String, dynamic>> _completedOrders = [];
|
||||
// List<Map<String, dynamic>> _completedOrders = [];
|
||||
List<Map<String, dynamic>> _closedOrders = [];
|
||||
List<Map<String, dynamic>> _refundingOrders = [];
|
||||
List<Map<String, dynamic>> _refundedOrders = [];
|
||||
List<Map<String, dynamic>> _cancelledOrders = [];
|
||||
|
||||
// 每个Tab的分页状态
|
||||
Map<int, int> _pageNums = {};
|
||||
Map<int, int> _totalCounts = {};
|
||||
Map<int, bool> _hasMoreData = {};
|
||||
Map<int, bool> _isLoading = {};
|
||||
final Map<int, int> _pageNums = {};
|
||||
final Map<int, int> _totalCounts = {};
|
||||
final Map<int, bool> _hasMoreData = {};
|
||||
final Map<int, bool> _isLoading = {};
|
||||
|
||||
// 订单状态:0->待付款;1->待核销;2->已完成;3->已关闭;4->退款中;5->已退款 6->已取消 精确匹配
|
||||
List tabList = [
|
||||
{'id': '', 'name': "全部", 'index': 0},
|
||||
{'id': 0, 'name': "待付款", 'index': 1},
|
||||
{'id': 1, 'name': "待核销", 'index': 2},
|
||||
{'id': 2, 'name': "已完成", 'index': 3},
|
||||
{'id': 2, 'name': "已支付", 'index': 2},
|
||||
// {'id': 2, 'name': "已完成", 'index': 3},
|
||||
{'id': 3, 'name': "已关闭", 'index': 4},
|
||||
{'id': 4, 'name': "退款中", 'index': 5},
|
||||
{'id': 5, 'name': "已退款", 'index': 6},
|
||||
{'id': 6, 'name': "已取消", 'index': 7}
|
||||
];
|
||||
|
||||
late List orderStatusDict;
|
||||
|
||||
late ScrollController scrollController = ScrollController();
|
||||
late TabController tabController = TabController(initialIndex: 0, length: tabList.length, vsync: this);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
getOrderStatusDict();
|
||||
// 初始化分页状态
|
||||
for (var tab in tabList) {
|
||||
_pageNums[tab['index']] = 1;
|
||||
@ -83,19 +89,36 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> getOrderStatusDict() async {
|
||||
final res = await Http.get('${CommonApi.dictionaryApi}oms_order_status');
|
||||
orderStatusDict = res['data'] as List;
|
||||
//获取初始订单列表
|
||||
getOrderList(false);
|
||||
// dictValue
|
||||
}
|
||||
|
||||
// 获取当前Tab的订单列表
|
||||
List<Map<String, dynamic>> _getCurrentOrderList() {
|
||||
final currentIndex = tabController.index;
|
||||
switch (currentIndex) {
|
||||
case 0: return _allOrders;
|
||||
case 1: return _pendingPaymentOrders;
|
||||
case 2: return _pendingVerificationOrders;
|
||||
case 3: return _completedOrders;
|
||||
case 4: return _closedOrders;
|
||||
case 5: return _refundingOrders;
|
||||
case 6: return _refundedOrders;
|
||||
case 7: return _cancelledOrders;
|
||||
default: return _allOrders;
|
||||
case 0:
|
||||
return _allOrders;
|
||||
case 1:
|
||||
return _pendingPaymentOrders;
|
||||
case 2:
|
||||
return _pendingVerificationOrders;
|
||||
// case 3:
|
||||
// return _completedOrders;
|
||||
case 3:
|
||||
return _closedOrders;
|
||||
case 4:
|
||||
return _refundingOrders;
|
||||
case 5:
|
||||
return _refundedOrders;
|
||||
case 6:
|
||||
return _cancelledOrders;
|
||||
default:
|
||||
return _allOrders;
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,35 +148,35 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
|
||||
_pendingVerificationOrders = orders;
|
||||
}
|
||||
break;
|
||||
// case 3:
|
||||
// if (loadMore) {
|
||||
// _completedOrders.addAll(orders);
|
||||
// } else {
|
||||
// _completedOrders = orders;
|
||||
// }
|
||||
// break;
|
||||
case 3:
|
||||
if (loadMore) {
|
||||
_completedOrders.addAll(orders);
|
||||
} else {
|
||||
_completedOrders = orders;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (loadMore) {
|
||||
_closedOrders.addAll(orders);
|
||||
} else {
|
||||
_closedOrders = orders;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
case 4:
|
||||
if (loadMore) {
|
||||
_refundingOrders.addAll(orders);
|
||||
} else {
|
||||
_refundingOrders = orders;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
case 5:
|
||||
if (loadMore) {
|
||||
_refundedOrders.addAll(orders);
|
||||
} else {
|
||||
_refundedOrders = orders;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
case 6:
|
||||
if (loadMore) {
|
||||
_cancelledOrders.addAll(orders);
|
||||
} else {
|
||||
@ -167,9 +190,7 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
|
||||
// 滚动监听
|
||||
void _scrollListener() {
|
||||
final currentIndex = tabController.index;
|
||||
if (scrollController.position.pixels == scrollController.position.maxScrollExtent &&
|
||||
!_isLoading[currentIndex]! &&
|
||||
_hasMoreData[currentIndex]!) {
|
||||
if (scrollController.position.pixels == scrollController.position.maxScrollExtent && !_isLoading[currentIndex]! && _hasMoreData[currentIndex]!) {
|
||||
getOrderList(true);
|
||||
}
|
||||
}
|
||||
@ -212,6 +233,7 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
|
||||
|
||||
if (res['code'] == 200 && res['data'] != null) {
|
||||
final data = res['data'];
|
||||
logger.e(data);
|
||||
final List<Map<String, dynamic>> newOrders = List<Map<String, dynamic>>.from(data['records'] ?? []);
|
||||
final int total = data['total'] ?? 0;
|
||||
|
||||
@ -267,10 +289,10 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
|
||||
switch (statusCode) {
|
||||
case 0: // 待付款
|
||||
return '待付款: ';
|
||||
case 1: // 待核销
|
||||
case 2: // 待核销
|
||||
return '已付款: ';
|
||||
case 2: // 已完成
|
||||
return '实付款: ';
|
||||
// case 2: // 已完成
|
||||
// return '实付款: ';
|
||||
case 3: // 已关闭
|
||||
return '订单金额: ';
|
||||
case 4: // 退款中
|
||||
@ -310,13 +332,12 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
spacing: 5.0,
|
||||
children: [
|
||||
ClipOval(
|
||||
child: Image.asset(
|
||||
'assets/images/avatar/img11.jpg',
|
||||
width: 25.0,
|
||||
),
|
||||
),
|
||||
Text(order['items'][0]['tenantName'] ?? '商家名称'),
|
||||
// ClipOval(
|
||||
// child: NetworkOrAssetImage(
|
||||
// imageUrl: order['items'][0]['faceUrl'] ?? '',
|
||||
// width: 25,
|
||||
// )),
|
||||
Text(order['items'][0]['tenantName'] ?? '未知商家名称'),
|
||||
Icon(
|
||||
Icons.arrow_forward_ios_rounded,
|
||||
color: Colors.grey,
|
||||
@ -325,68 +346,75 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
|
||||
],
|
||||
),
|
||||
Spacer(),
|
||||
Text(
|
||||
_getStatusText(order['status']),
|
||||
style: TextStyle(color: _getStatusColor(order['status'])),
|
||||
)
|
||||
order['items'][0]['status'] == 1
|
||||
? Text(
|
||||
'已核销',
|
||||
style: TextStyle(color: Colors.red),
|
||||
)
|
||||
: Text(
|
||||
_getStatusText(order['status']),
|
||||
style: TextStyle(color: _getStatusColor(order['status'])),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
// 商品信息
|
||||
if (order['items'] != null && order['items'].isNotEmpty)
|
||||
...order['items'].map<Widget>((item) => Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 80.0,
|
||||
height: 80.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[200],
|
||||
borderRadius: BorderRadius.circular(4.0),
|
||||
),
|
||||
child: order['items'][0]['pic'] != null && order['items'][0]['pic'].isNotEmpty
|
||||
? Image.network(
|
||||
order['items'][0]['pic'],
|
||||
width: 80.0,
|
||||
height: 80.0,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: Icon(
|
||||
Icons.shopping_bag_outlined,
|
||||
size: 40.0,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item['productName'] ?? '商品名称',
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 14),
|
||||
...order['items']
|
||||
.map<Widget>((item) => Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 80.0,
|
||||
height: 80.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[200],
|
||||
borderRadius: BorderRadius.circular(4.0),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Row(
|
||||
child: order['items'][0]['pic'] != null && order['items'][0]['pic'].isNotEmpty
|
||||
? Image.network(
|
||||
order['items'][0]['pic'],
|
||||
width: 80.0,
|
||||
height: 80.0,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: Icon(
|
||||
Icons.shopping_bag_outlined,
|
||||
size: 40.0,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'¥${item['salePrice'] ?? '0'}',
|
||||
style: TextStyle(color: Colors.red, fontSize: 16),
|
||||
item['productName'] ?? '商品名称',
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
Spacer(),
|
||||
Text(
|
||||
'x${item['quantity'] ?? '1'}',
|
||||
style: TextStyle(color: Colors.grey),
|
||||
SizedBox(height: 5),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'¥${item['salePrice'] ?? '0'}',
|
||||
style: TextStyle(color: Colors.red, fontSize: 16),
|
||||
),
|
||||
Spacer(),
|
||||
Text(
|
||||
'x${item['quantity'] ?? '1'}',
|
||||
style: TextStyle(color: Colors.grey),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
)).toList(),
|
||||
)
|
||||
],
|
||||
))
|
||||
.toList(),
|
||||
SizedBox(height: 10),
|
||||
// 金额信息
|
||||
Container(
|
||||
@ -422,8 +450,18 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
|
||||
);
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
String _getStatusText(dynamic status) {
|
||||
int statusCode = status is String ? int.tryParse(status) ?? -1 : (status is int ? status : -1);
|
||||
final match = orderStatusDict.firstWhere(
|
||||
(item) => item['dictValue'].toString() == statusCode.toString(),
|
||||
orElse: () => {'dictLabel': '未知状态'},
|
||||
);
|
||||
|
||||
return match['dictLabel'] ?? '未知状态';
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
String _getStatusTextold(dynamic status) {
|
||||
// 处理字符串或数字类型的状态
|
||||
int statusCode = status is String ? int.tryParse(status) ?? 0 : (status is int ? status : 0);
|
||||
|
||||
@ -515,7 +553,7 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
|
||||
void _cancelOrder(String orderId) async {
|
||||
try {
|
||||
final res = await Http.post('${ShopApi.cancelGoodsOrder}/$orderId');
|
||||
print('取消订单成功-------------->${res}');
|
||||
print('取消订单成功-------------->$res');
|
||||
|
||||
if (res['code'] == 200) {
|
||||
MyToast().tip(
|
||||
@ -527,7 +565,7 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
|
||||
_refreshAllRelatedTabs();
|
||||
}
|
||||
} catch (e) {
|
||||
print('取消订单失败-------------->${e}');
|
||||
print('取消订单失败-------------->$e');
|
||||
}
|
||||
}
|
||||
|
||||
@ -546,9 +584,61 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
|
||||
}
|
||||
}
|
||||
|
||||
void _payOrder(_orderId) {
|
||||
// 打开微信小程序的某个页面地址,如pages/index/index
|
||||
Wxsdk.openMiniApp(orderId: _orderId);
|
||||
void _payOrder(orderId) async {
|
||||
// 打开微信小程序的某个页面地址,如pages/index/index
|
||||
// Wxsdk.openMiniApp(orderId: orderId);
|
||||
|
||||
//------------
|
||||
final infoCtl = Get.find<ImUserInfoController>();
|
||||
final openId = infoCtl.customInfo['openId'];
|
||||
if (openId == null || openId.isEmpty) {
|
||||
showDialog(
|
||||
context: Get.context!,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text('微信授权'),
|
||||
content: const Text('当前支付需要您进行微信授权', style: TextStyle(fontSize: 16.0)),
|
||||
backgroundColor: Colors.white,
|
||||
surfaceTintColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
|
||||
elevation: 2.0,
|
||||
actionsPadding: const EdgeInsets.all(15.0),
|
||||
actions: [
|
||||
TextButton(onPressed: () => {Get.back()}, child: Text('取消', style: TextStyle(color: Colors.red))),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
//去授权
|
||||
await Wxsdk.login();
|
||||
Get.back();
|
||||
},
|
||||
child: Text('去授权')),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
//------
|
||||
|
||||
final data = {
|
||||
"orderType": "ORDER",
|
||||
"clientType": "APP",
|
||||
"paymentMethod": "WECHAT",
|
||||
"paymentClient": "APP",
|
||||
"sn": orderId,
|
||||
};
|
||||
final res = await Http.post(CommonApi.wechatPay, data: data);
|
||||
final payParams = res['data'];
|
||||
// 拉起支付
|
||||
await Wxsdk.payWithWx(
|
||||
appId: payParams['appid'],
|
||||
partnerId: payParams['partnerid'],
|
||||
prepayId: payParams['prepayid'],
|
||||
packageValue: payParams['package'],
|
||||
nonceStr: payParams['noncestr'],
|
||||
timestamp: int.parse(payParams['timestamp']),
|
||||
sign: payParams['sign'],
|
||||
);
|
||||
}
|
||||
|
||||
// 重置指定tab的数据
|
||||
@ -560,14 +650,30 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
|
||||
_hasMoreData[index] = true;
|
||||
|
||||
switch (index) {
|
||||
case 0: _allOrders = []; break;
|
||||
case 1: _pendingPaymentOrders = []; break;
|
||||
case 2: _pendingVerificationOrders = []; break;
|
||||
case 3: _completedOrders = []; break;
|
||||
case 4: _closedOrders = []; break;
|
||||
case 5: _refundingOrders = []; break;
|
||||
case 6: _refundedOrders = []; break;
|
||||
case 7: _cancelledOrders = []; break;
|
||||
case 0:
|
||||
_allOrders = [];
|
||||
break;
|
||||
case 1:
|
||||
_pendingPaymentOrders = [];
|
||||
break;
|
||||
case 2:
|
||||
_pendingVerificationOrders = [];
|
||||
break;
|
||||
// case 3:
|
||||
// _completedOrders = [];
|
||||
// break;
|
||||
case 3:
|
||||
_closedOrders = [];
|
||||
break;
|
||||
case 4:
|
||||
_refundingOrders = [];
|
||||
break;
|
||||
case 5:
|
||||
_refundedOrders = [];
|
||||
break;
|
||||
case 6:
|
||||
_cancelledOrders = [];
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -586,44 +692,44 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
|
||||
bottom: PreferredSize(
|
||||
preferredSize: Size.fromHeight(45.0),
|
||||
child: Container(
|
||||
height: 45.0,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: TabBar(
|
||||
controller: tabController,
|
||||
tabAlignment: TabAlignment.start,
|
||||
isScrollable: true,
|
||||
padding: EdgeInsets.only(left: 0),
|
||||
indicatorPadding: EdgeInsets.zero,
|
||||
labelPadding: EdgeInsets.symmetric(horizontal: 10.0),
|
||||
tabs: tabList
|
||||
.map((item) => Container(
|
||||
constraints: BoxConstraints(minWidth: 70),
|
||||
alignment: Alignment.center,
|
||||
child: Badge.count(
|
||||
backgroundColor: Colors.red,
|
||||
offset: Offset(14, -4),
|
||||
count: item['badge'] ?? 0,
|
||||
isLabelVisible: item['badge'] != null && item['badge'] > 0,
|
||||
child: Text(
|
||||
item['name'],
|
||||
style: TextStyle(fontSize: 16),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
height: 45.0,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: TabBar(
|
||||
controller: tabController,
|
||||
tabAlignment: TabAlignment.start,
|
||||
isScrollable: true,
|
||||
padding: EdgeInsets.only(left: 0),
|
||||
indicatorPadding: EdgeInsets.zero,
|
||||
labelPadding: EdgeInsets.symmetric(horizontal: 10.0),
|
||||
tabs: tabList
|
||||
.map((item) => Container(
|
||||
constraints: BoxConstraints(minWidth: 70),
|
||||
alignment: Alignment.center,
|
||||
child: Badge.count(
|
||||
backgroundColor: Colors.red,
|
||||
offset: Offset(14, -4),
|
||||
count: item['badge'] ?? 0,
|
||||
isLabelVisible: item['badge'] != null && item['badge'] > 0,
|
||||
child: Text(
|
||||
item['name'],
|
||||
style: TextStyle(fontSize: 16),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
||||
unselectedLabelColor: Colors.black87,
|
||||
labelColor: Color(0xFFFF5000),
|
||||
indicator: UnderlineTabIndicator(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
borderSide: BorderSide(color: Color(0xFFFF5000), width: 2.0),
|
||||
),
|
||||
indicatorSize: TabBarIndicatorSize.label,
|
||||
unselectedLabelStyle: TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei'),
|
||||
labelStyle: TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei', fontWeight: FontWeight.w700),
|
||||
dividerHeight: 0,
|
||||
),
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
||||
unselectedLabelColor: Colors.black87,
|
||||
labelColor: Color(0xFFFF5000),
|
||||
indicator: UnderlineTabIndicator(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
borderSide: BorderSide(color: Color(0xFFFF5000), width: 2.0),
|
||||
),
|
||||
indicatorSize: TabBarIndicatorSize.label,
|
||||
unselectedLabelStyle: TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei'),
|
||||
labelStyle: TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei', fontWeight: FontWeight.w700),
|
||||
dividerHeight: 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -638,49 +744,55 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
|
||||
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||
child: Container(
|
||||
color: Colors.grey[50],
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final currentOrders = _getOrderListByIndex(index);
|
||||
final isLoading = _isLoading[index] ?? false;
|
||||
final hasMoreData = _hasMoreData[index] ?? false;
|
||||
child: Builder(builder: (context) {
|
||||
final currentOrders = _getOrderListByIndex(index);
|
||||
final isLoading = _isLoading[index] ?? false;
|
||||
final hasMoreData = _hasMoreData[index] ?? false;
|
||||
|
||||
return currentOrders.isEmpty && !isRefreshing
|
||||
? emptyTip()
|
||||
: ListView.builder(
|
||||
controller: scrollController,
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.all(10.0),
|
||||
itemCount: currentOrders.length + (hasMoreData ? 1 : 0),
|
||||
itemBuilder: (context, itemIndex) {
|
||||
if (itemIndex == currentOrders.length) {
|
||||
return _buildLoadMoreIndicator(isLoading);
|
||||
}
|
||||
return _buildOrderItem(currentOrders[itemIndex]);
|
||||
},
|
||||
);
|
||||
}
|
||||
),
|
||||
return currentOrders.isEmpty && !isRefreshing
|
||||
? emptyTip()
|
||||
: ListView.builder(
|
||||
controller: scrollController,
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.all(10.0),
|
||||
itemCount: currentOrders.length + (hasMoreData ? 1 : 0),
|
||||
itemBuilder: (context, itemIndex) {
|
||||
if (itemIndex == currentOrders.length) {
|
||||
return _buildLoadMoreIndicator(isLoading);
|
||||
}
|
||||
return _buildOrderItem(currentOrders[itemIndex]);
|
||||
},
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
// 根据索引获取订单列表
|
||||
List<Map<String, dynamic>> _getOrderListByIndex(int index) {
|
||||
switch (index) {
|
||||
case 0: return _allOrders;
|
||||
case 1: return _pendingPaymentOrders;
|
||||
case 2: return _pendingVerificationOrders;
|
||||
case 3: return _completedOrders;
|
||||
case 4: return _closedOrders;
|
||||
case 5: return _refundingOrders;
|
||||
case 6: return _refundedOrders;
|
||||
case 7: return _cancelledOrders;
|
||||
default: return _allOrders;
|
||||
case 0:
|
||||
return _allOrders;
|
||||
case 1:
|
||||
return _pendingPaymentOrders;
|
||||
case 2:
|
||||
return _pendingVerificationOrders;
|
||||
// case 3:
|
||||
// return _completedOrders;
|
||||
case 3:
|
||||
return _closedOrders;
|
||||
case 4:
|
||||
return _refundingOrders;
|
||||
case 5:
|
||||
return _refundedOrders;
|
||||
case 6:
|
||||
return _cancelledOrders;
|
||||
default:
|
||||
return _allOrders;
|
||||
}
|
||||
}
|
||||
|
||||
@ -708,7 +820,7 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
|
||||
}
|
||||
|
||||
Widget emptyTip() {
|
||||
return Container(
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
library;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/IM/push_service.dart';
|
||||
import 'package:loopin/api/common_api.dart';
|
||||
import 'package:loopin/api/shop_api.dart';
|
||||
import 'package:shirne_dialog/shirne_dialog.dart';
|
||||
import 'package:loopin/components/my_toast.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:shirne_dialog/shirne_dialog.dart';
|
||||
|
||||
import '../../behavior/custom_scroll_behavior.dart';
|
||||
|
||||
class SellerOrderDetail extends StatefulWidget {
|
||||
@ -16,28 +20,27 @@ class SellerOrderDetail extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTickerProviderStateMixin {
|
||||
late String ? _orderId;
|
||||
late String ?_writeOffCodeId;
|
||||
late List orderStatusDict;
|
||||
|
||||
late String _orderId;
|
||||
late String _writeOffCodeId;
|
||||
dynamic orderGoodsInfo;
|
||||
bool _isLoading = true; // 添加加载状态
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_orderId = Get.arguments['orderId'] ?? ''; // 从商家订单列表进入此页面,此时带着订单id
|
||||
_writeOffCodeId = Get.arguments['writeOffCodeId'] ?? ''; // 从扫码核销进入此页面,此时带着核销码
|
||||
if(_orderId != null){
|
||||
getOrderDetailByOrderId(_orderId);
|
||||
}else if(_writeOffCodeId != null){
|
||||
getOrderDetailByWriteOffId(_writeOffCodeId);
|
||||
}
|
||||
|
||||
getOrderStatusDict();
|
||||
// _orderId = Get.arguments['orderId'] ?? ''; // 从商家订单列表进入此页面,此时带着订单id
|
||||
// _writeOffCodeId = Get.arguments['writeOffCodeId'] ?? ''; // 从扫码核销进入此页面,此时带着核销码
|
||||
// if (_orderId.isNotEmpty) {
|
||||
// getOrderDetailByOrderId(_orderId);
|
||||
// } else if (_writeOffCodeId.isNotEmpty) {
|
||||
// getOrderDetailByWriteOffId(_writeOffCodeId);
|
||||
// }
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
// 获取订单详情信息,根据订单id
|
||||
void getOrderDetailByOrderId(orderId) async {
|
||||
try {
|
||||
@ -45,7 +48,7 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
|
||||
_isLoading = true;
|
||||
});
|
||||
final res = await Http.get('${ShopApi.goodsOrderDetail}/$orderId');
|
||||
debugPrint(res['data'].toString(), wrapWidth: 600);
|
||||
debugPrint(res['data'].toString(), wrapWidth: 600);
|
||||
setState(() {
|
||||
orderGoodsInfo = res['data'];
|
||||
_isLoading = false;
|
||||
@ -57,14 +60,30 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
|
||||
MyDialog.toast('获取订单详情失败');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getOrderStatusDict() async {
|
||||
final res = await Http.get('${CommonApi.dictionaryApi}oms_order_status');
|
||||
// logger.e(res);
|
||||
orderStatusDict = res['data'] as List;
|
||||
//获取订单数据
|
||||
_orderId = Get.arguments['orderId'] ?? ''; // 从商家订单列表进入此页面,此时带着订单id
|
||||
_writeOffCodeId = Get.arguments['writeOffCodeId'] ?? ''; // 从扫码核销进入此页面,此时带着核销码
|
||||
if (_orderId.isNotEmpty) {
|
||||
getOrderDetailByOrderId(_orderId);
|
||||
} else if (_writeOffCodeId.isNotEmpty) {
|
||||
getOrderDetailByWriteOffId(_writeOffCodeId);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取订单详情信息,根据核销码
|
||||
void getOrderDetailByWriteOffId(code) async {
|
||||
try {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
logger.e('code=$code');
|
||||
final res = await Http.get('${ShopApi.getWriteOffDetail}?code=$code');
|
||||
print('订单详情-------------->${res['data']}');
|
||||
logger.w('订单详情-------------->${res['data']}');
|
||||
setState(() {
|
||||
orderGoodsInfo = res['data'];
|
||||
_isLoading = false;
|
||||
@ -78,45 +97,59 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
|
||||
}
|
||||
|
||||
// 确认核销订单
|
||||
void _confirmVerifyCode () async {
|
||||
late String writeOffCode;
|
||||
if(_orderId != null){ // 商家可以直接从订单详情获取核销码,并过滤后进行核销
|
||||
var verificationCodesList = orderGoodsInfo['verificationCodes'];
|
||||
if (verificationCodesList != null && verificationCodesList.isNotEmpty) {
|
||||
// 过滤可用的核销码(status为0)
|
||||
List<dynamic> newVerifyList = verificationCodesList.where((item) => item['status'] == 0).toList();
|
||||
if (newVerifyList.isNotEmpty) {
|
||||
writeOffCode = newVerifyList[0]['code'] ?? '';
|
||||
} else {
|
||||
return MyToast().tip(
|
||||
title: '暂无可用的核销码',
|
||||
position: 'center',
|
||||
type: 'error',
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return MyToast().tip(
|
||||
title: '暂无可用的核销码',
|
||||
position: 'center',
|
||||
type: 'error',
|
||||
);
|
||||
}
|
||||
}else if(_writeOffCodeId != null){
|
||||
writeOffCode = _writeOffCodeId?? '';
|
||||
}
|
||||
try {
|
||||
final res = await Http.get('${ShopApi.confirmWriteOff}?code=$writeOffCode');
|
||||
debugPrint(res['data'].toString(), wrapWidth: 600);
|
||||
void _confirmVerifyCode() async {
|
||||
logger.e(orderGoodsInfo);
|
||||
final status = orderGoodsInfo['verificationCodeStatus'];
|
||||
final code = orderGoodsInfo['code'];
|
||||
if (status == 0) {
|
||||
try {
|
||||
final res = await Http.get('${ShopApi.confirmWriteOff}?code=$code');
|
||||
logger.e(res);
|
||||
MyToast().tip(
|
||||
title: '核销成功',
|
||||
position: 'center',
|
||||
type: 'error',
|
||||
type: 'success',
|
||||
);
|
||||
} catch (e) {
|
||||
logger.e('失败:$e');
|
||||
}
|
||||
}
|
||||
// // 商家可以直接从订单详情获取核销码,并过滤后进行核销
|
||||
// var verificationCodesList = orderGoodsInfo['verificationCodes'];
|
||||
// logger.e(verificationCodesList);
|
||||
// if (verificationCodesList != null && verificationCodesList.isNotEmpty) {
|
||||
// // 过滤可用的核销码(status为0)
|
||||
// List<dynamic> newVerifyList = verificationCodesList.where((item) => item['status'] == 0).toList();
|
||||
// if (newVerifyList.isNotEmpty) {
|
||||
// writeOffCode = newVerifyList[0]['code'] ?? '';
|
||||
// } else {
|
||||
// return MyToast().tip(
|
||||
// title: '暂无可用的核销码',
|
||||
// position: 'center',
|
||||
// type: 'error',
|
||||
// );
|
||||
// }
|
||||
// } else {
|
||||
// return MyToast().tip(
|
||||
// title: '暂无可用的核销码',
|
||||
// position: 'center',
|
||||
// type: 'error',
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
||||
String getOrderStatusText(dynamic status) {
|
||||
int statusCode = status is String ? int.tryParse(status) ?? -1 : (status is int ? status : -1);
|
||||
final match = orderStatusDict.firstWhere(
|
||||
(item) => item['dictValue'].toString() == statusCode.toString(),
|
||||
orElse: () => {'dictLabel': '未知状态'},
|
||||
);
|
||||
|
||||
return match['dictLabel'] ?? '未知状态';
|
||||
}
|
||||
|
||||
// 获取订单状态文本
|
||||
String getOrderStatusText(int status) {
|
||||
String _getOrderStatusText_old(int status) {
|
||||
switch (status) {
|
||||
case 0:
|
||||
return '待付款';
|
||||
@ -161,14 +194,18 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
|
||||
|
||||
// 获取商品信息列表
|
||||
List<dynamic> getProductInfoList() {
|
||||
// 优先使用 items 字段
|
||||
// 优先使用 items 字段 --------
|
||||
if (orderGoodsInfo?['items'] != null && orderGoodsInfo!['items'] is List) {
|
||||
return orderGoodsInfo!['items'];
|
||||
}
|
||||
// 如果 items 为空,使用 productInfo 字段
|
||||
// 如果 items 为空,使用 productInfo 字段---------
|
||||
if (orderGoodsInfo?['productInfo'] != null && orderGoodsInfo!['productInfo'] is List) {
|
||||
return orderGoodsInfo!['productInfo'];
|
||||
}
|
||||
// 扫码进入
|
||||
if (orderGoodsInfo != null) {
|
||||
return [orderGoodsInfo];
|
||||
}
|
||||
// 如果都为空,返回空列表
|
||||
return [];
|
||||
}
|
||||
@ -230,9 +267,7 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
|
||||
),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(
|
||||
value: loadingProgress.expectedTotalBytes != null
|
||||
? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes!
|
||||
: null,
|
||||
value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null,
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -310,14 +345,14 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
|
||||
children: [],
|
||||
);
|
||||
|
||||
case 1: // 待核销
|
||||
case 2: // 待核销
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
const SizedBox(width: 10.0),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
_confirmVerifyCode();
|
||||
_confirmVerifyCode();
|
||||
},
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all(Color(0xFFFF5000)),
|
||||
@ -328,11 +363,11 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
|
||||
],
|
||||
);
|
||||
|
||||
case 2: // 已完成
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [],
|
||||
);
|
||||
// case 2: // 已完成
|
||||
// return Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.end,
|
||||
// children: [],
|
||||
// );
|
||||
|
||||
case 3: // 已关闭
|
||||
return Row(
|
||||
@ -432,8 +467,6 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
|
||||
style: TextStyle(color: Colors.orange),
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
|
||||
|
||||
],
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
@ -461,13 +494,21 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
|
||||
Row(
|
||||
children: [
|
||||
Spacer(),
|
||||
Text(
|
||||
getOrderStatusText(orderGoodsInfo?['status'] ?? 0),
|
||||
style: TextStyle(
|
||||
color: getOrderStatusColor(orderGoodsInfo?['status'] ?? 0),
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
)
|
||||
orderGoodsInfo['items'][0]['status'] == 1
|
||||
? Text(
|
||||
'已核销',
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
getOrderStatusText(orderGoodsInfo?['status'] ?? 0),
|
||||
style: TextStyle(
|
||||
color: getOrderStatusColor(orderGoodsInfo?['status'] ?? 0),
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
@ -507,7 +548,7 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
|
||||
size: 18.0,
|
||||
),
|
||||
onTap: () async {
|
||||
await Clipboard.setData(ClipboardData(text: _orderId??''));
|
||||
await Clipboard.setData(ClipboardData(text: _orderId ?? ''));
|
||||
MyDialog.toast('订单已复制到剪切板', icon: Icon(Icons.check_circle));
|
||||
},
|
||||
)
|
||||
@ -519,8 +560,11 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
|
||||
_buildOrderInfoRow('订单号', orderGoodsInfo?['orderId']?.toString() ?? ''),
|
||||
_buildOrderInfoRow('下单时间', orderGoodsInfo?['createTime']?.toString() ?? ''),
|
||||
_buildOrderInfoRow('购买数量', _calculateTotalQuantity().toString()),
|
||||
_buildOrderInfoRow('订单金额', '¥${orderGoodsInfo?['totalAmount']?.toString() ?? '0.00'}'),
|
||||
_buildOrderInfoRow('实付金额', '¥${orderGoodsInfo?['payAmount']?.toString() ?? '0.00'}'),
|
||||
_orderId.isNotEmpty
|
||||
? _buildOrderInfoRow('订单金额', '¥${orderGoodsInfo?['totalAmount']?.toString() ?? '0.00'}')
|
||||
: _buildOrderInfoRow('订单金额', '¥${orderGoodsInfo?['salePrice']?.toString() ?? '0.00'}'),
|
||||
|
||||
// _buildOrderInfoRow('实付金额', '¥${orderGoodsInfo?['payAmount']?.toString() ?? '0.00'}'),
|
||||
],
|
||||
)
|
||||
],
|
||||
@ -538,7 +582,7 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
|
||||
height: 60.0,
|
||||
color: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
|
||||
// child: buildBottomButtons(),
|
||||
child: _orderId.isEmpty ? buildBottomButtons() : null,
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -546,10 +590,18 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
|
||||
|
||||
// 计算总购买数量
|
||||
int _calculateTotalQuantity() {
|
||||
// final productList = getProductInfoList();
|
||||
// int total = 0;
|
||||
// for (var product in productList) {
|
||||
// total += (product?['buyNum'] as int? ?? 0);
|
||||
// }
|
||||
// return total;
|
||||
|
||||
final productList = getProductInfoList();
|
||||
int total = 0;
|
||||
// logger.e(productList);
|
||||
var total = 0;
|
||||
for (var product in productList) {
|
||||
total += (product?['buyNum'] as int? ?? 0);
|
||||
total = product['quantity'];
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
@ -3,7 +3,8 @@ library;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/utils/wxsdk.dart';
|
||||
import 'package:loopin/IM/push_service.dart';
|
||||
import 'package:loopin/api/common_api.dart';
|
||||
import 'package:loopin/api/shop_api.dart';
|
||||
import 'package:loopin/components/my_toast.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
@ -28,33 +29,45 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
List<Map<String, dynamic>> _allOrders = [];
|
||||
List<Map<String, dynamic>> _pendingPaymentOrders = [];
|
||||
List<Map<String, dynamic>> _pendingVerificationOrders = [];
|
||||
List<Map<String, dynamic>> _completedOrders = [];
|
||||
// List<Map<String, dynamic>> _completedOrders = [];
|
||||
List<Map<String, dynamic>> _closedOrders = [];
|
||||
List<Map<String, dynamic>> _refundingOrders = [];
|
||||
List<Map<String, dynamic>> _refundedOrders = [];
|
||||
List<Map<String, dynamic>> _cancelledOrders = [];
|
||||
|
||||
// 每个Tab的分页状态
|
||||
Map<int, int> _pageNums = {};
|
||||
Map<int, int> _totalCounts = {};
|
||||
Map<int, bool> _hasMoreData = {};
|
||||
Map<int, bool> _isLoading = {};
|
||||
final Map<int, int> _pageNums = {};
|
||||
final Map<int, int> _totalCounts = {};
|
||||
final Map<int, bool> _hasMoreData = {};
|
||||
final Map<int, bool> _isLoading = {};
|
||||
|
||||
// 订单状态:0->待付款;1->待核销;2->已完成;3->已关闭;4->退款中;5->已退款 6->已取消 精确匹配
|
||||
// List tabList = [
|
||||
// {'id': '', 'name': "全部", 'index': 0},
|
||||
// {'id': 0, 'name': "待付款", 'index': 1},
|
||||
// {'id': 1, 'name': "待核销", 'index': 2},
|
||||
// {'id': 2, 'name': "已完成", 'index': 3},
|
||||
// {'id': 3, 'name': "已关闭", 'index': 4},
|
||||
// {'id': 4, 'name': "退款中", 'index': 5},
|
||||
// {'id': 5, 'name': "已退款", 'index': 6},
|
||||
// {'id': 6, 'name': "已取消", 'index': 7}
|
||||
// ];
|
||||
List tabList = [
|
||||
{'id': '', 'name': "全部", 'index': 0},
|
||||
{'id': 0, 'name': "待付款", 'index': 1},
|
||||
{'id': 1, 'name': "待核销", 'index': 2},
|
||||
{'id': 2, 'name': "已完成", 'index': 3},
|
||||
{'id': 3, 'name': "已关闭", 'index': 4},
|
||||
{'id': 4, 'name': "退款中", 'index': 5},
|
||||
{'id': 5, 'name': "已退款", 'index': 6},
|
||||
{'id': 6, 'name': "已取消", 'index': 7}
|
||||
{'id': 2, 'name': "已支付", 'index': 2},
|
||||
// {'id': 2, 'name': "已完成", 'index': 3},
|
||||
{'id': 3, 'name': "已关闭", 'index': 3},
|
||||
{'id': 4, 'name': "退款中", 'index': 4},
|
||||
{'id': 5, 'name': "已退款", 'index': 5},
|
||||
{'id': 6, 'name': "已取消", 'index': 6}
|
||||
];
|
||||
|
||||
late ScrollController scrollController = ScrollController();
|
||||
late TabController tabController = TabController(initialIndex: 0, length: tabList.length, vsync: this);
|
||||
|
||||
late List orderStatusDict;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -70,8 +83,9 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
scrollController.addListener(_scrollListener);
|
||||
// 监听tab切换事件
|
||||
tabController.addListener(_tabChanged);
|
||||
getOrderStatusDict();
|
||||
// 获取初始订单列表
|
||||
getOrderList(false);
|
||||
// getOrderList(false);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -83,19 +97,37 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> getOrderStatusDict() async {
|
||||
final res = await Http.get('${CommonApi.dictionaryApi}oms_order_status');
|
||||
logger.e(res);
|
||||
orderStatusDict = res['data'] as List;
|
||||
//获取初始订单列表
|
||||
getOrderList(false);
|
||||
// dictValue
|
||||
}
|
||||
|
||||
// 获取当前Tab的订单列表
|
||||
List<Map<String, dynamic>> _getCurrentOrderList() {
|
||||
final currentIndex = tabController.index;
|
||||
switch (currentIndex) {
|
||||
case 0: return _allOrders;
|
||||
case 1: return _pendingPaymentOrders;
|
||||
case 2: return _pendingVerificationOrders;
|
||||
case 3: return _completedOrders;
|
||||
case 4: return _closedOrders;
|
||||
case 5: return _refundingOrders;
|
||||
case 6: return _refundedOrders;
|
||||
case 7: return _cancelledOrders;
|
||||
default: return _allOrders;
|
||||
case 0:
|
||||
return _allOrders;
|
||||
case 1:
|
||||
return _pendingPaymentOrders;
|
||||
case 2:
|
||||
return _pendingVerificationOrders;
|
||||
// case 3:
|
||||
// return _completedOrders;
|
||||
case 3:
|
||||
return _closedOrders;
|
||||
case 4:
|
||||
return _refundingOrders;
|
||||
case 5:
|
||||
return _refundedOrders;
|
||||
case 6:
|
||||
return _cancelledOrders;
|
||||
default:
|
||||
return _allOrders;
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,35 +157,35 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
_pendingVerificationOrders = orders;
|
||||
}
|
||||
break;
|
||||
// case 3:
|
||||
// if (loadMore) {
|
||||
// _completedOrders.addAll(orders);
|
||||
// } else {
|
||||
// _completedOrders = orders;
|
||||
// }
|
||||
// break;
|
||||
case 3:
|
||||
if (loadMore) {
|
||||
_completedOrders.addAll(orders);
|
||||
} else {
|
||||
_completedOrders = orders;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (loadMore) {
|
||||
_closedOrders.addAll(orders);
|
||||
} else {
|
||||
_closedOrders = orders;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
case 4:
|
||||
if (loadMore) {
|
||||
_refundingOrders.addAll(orders);
|
||||
} else {
|
||||
_refundingOrders = orders;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
case 5:
|
||||
if (loadMore) {
|
||||
_refundedOrders.addAll(orders);
|
||||
} else {
|
||||
_refundedOrders = orders;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
case 6:
|
||||
if (loadMore) {
|
||||
_cancelledOrders.addAll(orders);
|
||||
} else {
|
||||
@ -167,9 +199,7 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
// 滚动监听
|
||||
void _scrollListener() {
|
||||
final currentIndex = tabController.index;
|
||||
if (scrollController.position.pixels == scrollController.position.maxScrollExtent &&
|
||||
!_isLoading[currentIndex]! &&
|
||||
_hasMoreData[currentIndex]!) {
|
||||
if (scrollController.position.pixels == scrollController.position.maxScrollExtent && !_isLoading[currentIndex]! && _hasMoreData[currentIndex]!) {
|
||||
getOrderList(true);
|
||||
}
|
||||
}
|
||||
@ -179,6 +209,7 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
if (tabController.index != tabController.previousIndex) {
|
||||
// 如果当前Tab没有数据,则加载数据
|
||||
final currentOrders = _getCurrentOrderList();
|
||||
|
||||
if (currentOrders.isEmpty) {
|
||||
getOrderList(false);
|
||||
}
|
||||
@ -207,16 +238,17 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
final res = await Http.post(ShopApi.sellerOrderList, data: {
|
||||
'current': _pageNums[currentIndex], // 当前页码
|
||||
'size': pageSize,
|
||||
'status': status == '' ? '' : status.toString(),
|
||||
'orderStatus': status == '' ? '' : status.toString(),
|
||||
});
|
||||
logger.w(status);
|
||||
|
||||
logger.w(res);
|
||||
|
||||
if (res['code'] == 200 && res['data'] != null) {
|
||||
final data = res['data'];
|
||||
final List<Map<String, dynamic>> newOrders = List<Map<String, dynamic>>.from(data['records'] ?? []);
|
||||
final int total = data['total'] ?? 0;
|
||||
|
||||
debugPrint(data.toString(), wrapWidth: 1024);
|
||||
|
||||
// 更新当前Tab的数据
|
||||
_setCurrentOrderList(newOrders, loadMore);
|
||||
|
||||
@ -236,7 +268,7 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print('获取订单列表失败: $e');
|
||||
logger.w('获取订单列表失败: $e');
|
||||
setState(() {
|
||||
_hasMoreData[currentIndex] = false;
|
||||
});
|
||||
@ -266,11 +298,11 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
|
||||
switch (statusCode) {
|
||||
case 0: // 待付款
|
||||
return '待付款: ';
|
||||
case 1: // 待核销
|
||||
return '用户待付款: ';
|
||||
case 2: // 待核销
|
||||
return '已付款: ';
|
||||
case 2: // 已完成
|
||||
return '实付款: ';
|
||||
// case 2: // 已完成
|
||||
// return '实付款: ';
|
||||
case 3: // 已关闭
|
||||
return '订单金额: ';
|
||||
case 4: // 退款中
|
||||
@ -310,13 +342,13 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
spacing: 5.0,
|
||||
children: [
|
||||
ClipOval(
|
||||
child: Image.asset(
|
||||
'assets/images/avatar/img11.jpg',
|
||||
width: 25.0,
|
||||
),
|
||||
),
|
||||
Text(order['items'][0]['tenantName'] ?? '商家名称'),
|
||||
// ClipOval(
|
||||
// child: Image.asset(
|
||||
// 'assets/images/avatar/img11.jpg',
|
||||
// width: 25.0,
|
||||
// ),
|
||||
// ),
|
||||
Text(order['tenantName'] ?? '商家名称'),
|
||||
Icon(
|
||||
Icons.arrow_forward_ios_rounded,
|
||||
color: Colors.grey,
|
||||
@ -325,74 +357,81 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
],
|
||||
),
|
||||
Spacer(),
|
||||
Text(
|
||||
_getStatusText(order['status']),
|
||||
style: TextStyle(color: _getStatusColor(order['status'])),
|
||||
)
|
||||
order['verificationCodeStatus'] == 1
|
||||
? Text(
|
||||
'已核销',
|
||||
style: TextStyle(color: Colors.red),
|
||||
)
|
||||
: Text(
|
||||
_getStatusText(order['status']),
|
||||
style: TextStyle(color: _getStatusColor(order['status'])),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
// 商品信息
|
||||
if (order['items'] != null && order['items'].isNotEmpty)
|
||||
...order['items'].map<Widget>((item) => Row(
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 80.0,
|
||||
height: 80.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(4.0),
|
||||
),
|
||||
child: order['pic'] != null && order['pic'].isNotEmpty
|
||||
? ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
child: Image.network(
|
||||
order['pic'],
|
||||
width: 80.0,
|
||||
height: 80.0,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
)
|
||||
: Icon(
|
||||
Icons.shopping_bag_outlined,
|
||||
size: 40.0,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 80.0,
|
||||
height: 80.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[200],
|
||||
borderRadius: BorderRadius.circular(4.0),
|
||||
),
|
||||
child: order['items'][0]['pic'] != null && order['items'][0]['pic'].isNotEmpty
|
||||
? Image.network(
|
||||
order['items'][0]['pic'],
|
||||
width: 80.0,
|
||||
height: 80.0,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: Icon(
|
||||
Icons.shopping_bag_outlined,
|
||||
size: 40.0,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
Text(
|
||||
order['productName'] ?? '商品名称',
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'¥${order['salePrice'] ?? '0'}',
|
||||
style: TextStyle(color: Colors.red, fontSize: 16),
|
||||
),
|
||||
Spacer(),
|
||||
Text(
|
||||
'x${order['quantity'] ?? '1'}',
|
||||
style: TextStyle(color: Colors.grey),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item['productName'] ?? '商品名称',
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'¥${item['salePrice'] ?? '0'}',
|
||||
style: TextStyle(color: Colors.red, fontSize: 16),
|
||||
),
|
||||
Spacer(),
|
||||
Text(
|
||||
'x${item['quantity'] ?? '1'}',
|
||||
style: TextStyle(color: Colors.grey),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
)).toList(),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
// 金额信息
|
||||
Container(
|
||||
padding: EdgeInsets.all(5.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[50],
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
),
|
||||
child: Row(
|
||||
@ -402,7 +441,7 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
TextSpan(children: [
|
||||
TextSpan(text: _getPaymentText(order['status'])), // 根据状态获取文案
|
||||
TextSpan(
|
||||
text: '¥${order['totalAmount'] ?? '0'}',
|
||||
text: '¥${order['salePrice'] ?? '0'}',
|
||||
style: TextStyle(color: Colors.red, fontSize: 16),
|
||||
),
|
||||
]),
|
||||
@ -417,16 +456,27 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
Get.toNamed('/sellerOrder/detail', arguments: {'orderId': order['id']});
|
||||
// logger.e(order);
|
||||
Get.toNamed('/sellerOrder/detail', arguments: {'orderId': order['orderId']});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
String _getStatusText(dynamic status) {
|
||||
int statusCode = status is String ? int.tryParse(status) ?? -1 : (status is int ? status : -1);
|
||||
final match = orderStatusDict.firstWhere(
|
||||
(item) => item['dictValue'].toString() == statusCode.toString(),
|
||||
orElse: () => {'dictLabel': '未知状态'},
|
||||
);
|
||||
|
||||
return match['dictLabel'] ?? '未知状态';
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
String _getStatusText_old(dynamic status) {
|
||||
// 处理字符串或数字类型的状态
|
||||
int statusCode = status is String ? int.tryParse(status) ?? 0 : (status is int ? status : 0);
|
||||
|
||||
switch (statusCode) {
|
||||
case 0:
|
||||
return '待付款';
|
||||
@ -479,33 +529,34 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
int statusCode = status is String ? int.tryParse(status) ?? 0 : (status is int ? status : 0);
|
||||
switch (statusCode) {
|
||||
case 0: // 待付款
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () => _cancelOrder(orderId),
|
||||
style: ButtonStyle(backgroundColor: WidgetStateProperty.all(Colors.white)),
|
||||
child: Text('取消订单'),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
ElevatedButton(
|
||||
onPressed: () => _payOrder(orderId),
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all(Color(0xff07c160)),
|
||||
foregroundColor: WidgetStateProperty.all(Colors.white),
|
||||
),
|
||||
child: Text('去支付'),
|
||||
),
|
||||
],
|
||||
);
|
||||
case 1: // 待核销
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
);
|
||||
case 2: // 已完成
|
||||
return SizedBox();
|
||||
// return Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.end,
|
||||
// children: [
|
||||
// ElevatedButton(
|
||||
// onPressed: () => _cancelOrder(orderId),
|
||||
// style: ButtonStyle(backgroundColor: WidgetStateProperty.all(Colors.white)),
|
||||
// child: Text('取消订单'),
|
||||
// ),
|
||||
// SizedBox(width: 10),
|
||||
// ElevatedButton(
|
||||
// onPressed: () => _payOrder(orderId),
|
||||
// style: ButtonStyle(
|
||||
// backgroundColor: WidgetStateProperty.all(Color(0xff07c160)),
|
||||
// foregroundColor: WidgetStateProperty.all(Colors.white),
|
||||
// ),
|
||||
// child: Text('用户待付款'),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
case 2: // 待核销
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
);
|
||||
// case 2: // 已完成
|
||||
// return Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.end,
|
||||
// );
|
||||
default:
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
@ -515,7 +566,7 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
void _cancelOrder(String orderId) async {
|
||||
try {
|
||||
final res = await Http.post('${ShopApi.cancelGoodsOrder}/$orderId');
|
||||
print('取消订单成功-------------->${res}');
|
||||
logger.w('取消订单成功-------------->$res');
|
||||
|
||||
if (res['code'] == 200) {
|
||||
MyToast().tip(
|
||||
@ -527,7 +578,7 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
_refreshAllRelatedTabs();
|
||||
}
|
||||
} catch (e) {
|
||||
print('取消订单失败-------------->${e}');
|
||||
logger.w('取消订单失败-------------->$e');
|
||||
}
|
||||
}
|
||||
|
||||
@ -546,9 +597,9 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
}
|
||||
}
|
||||
|
||||
void _payOrder(_orderId) {
|
||||
// 打开微信小程序的某个页面地址,如pages/index/index
|
||||
Wxsdk.openMiniApp(orderId: _orderId);
|
||||
void _payOrder(orderId) {
|
||||
// 打开微信小程序的某个页面地址,如pages/index/index
|
||||
// Wxsdk.openMiniApp(orderId: orderId);
|
||||
}
|
||||
|
||||
// 重置指定tab的数据
|
||||
@ -560,14 +611,30 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
_hasMoreData[index] = true;
|
||||
|
||||
switch (index) {
|
||||
case 0: _allOrders = []; break;
|
||||
case 1: _pendingPaymentOrders = []; break;
|
||||
case 2: _pendingVerificationOrders = []; break;
|
||||
case 3: _completedOrders = []; break;
|
||||
case 4: _closedOrders = []; break;
|
||||
case 5: _refundingOrders = []; break;
|
||||
case 6: _refundedOrders = []; break;
|
||||
case 7: _cancelledOrders = []; break;
|
||||
case 0:
|
||||
_allOrders = [];
|
||||
break;
|
||||
case 1:
|
||||
_pendingPaymentOrders = []; // 待支付
|
||||
break;
|
||||
case 2:
|
||||
_pendingVerificationOrders = []; // 待核销
|
||||
break;
|
||||
// case 3:
|
||||
// _completedOrders = []; // 已完成
|
||||
// break;
|
||||
case 3:
|
||||
_closedOrders = []; // 关闭
|
||||
break;
|
||||
case 4:
|
||||
_refundingOrders = []; // 退款中
|
||||
break;
|
||||
case 5:
|
||||
_refundedOrders = []; // 已退款
|
||||
break;
|
||||
case 6:
|
||||
_cancelledOrders = []; // 已取消
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -586,60 +653,61 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
bottom: PreferredSize(
|
||||
preferredSize: Size.fromHeight(45.0),
|
||||
child: Container(
|
||||
height: 45.0,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: TabBar(
|
||||
controller: tabController,
|
||||
tabAlignment: TabAlignment.start,
|
||||
isScrollable: true,
|
||||
padding: EdgeInsets.only(left: 0),
|
||||
indicatorPadding: EdgeInsets.zero,
|
||||
labelPadding: EdgeInsets.symmetric(horizontal: 10.0),
|
||||
tabs: tabList
|
||||
.map((item) => Container(
|
||||
constraints: BoxConstraints(minWidth: 70),
|
||||
alignment: Alignment.center,
|
||||
child: Badge.count(
|
||||
backgroundColor: Colors.red,
|
||||
offset: Offset(14, -4),
|
||||
count: item['badge'] ?? 0,
|
||||
isLabelVisible: item['badge'] != null && item['badge'] > 0,
|
||||
child: Text(
|
||||
item['name'],
|
||||
style: TextStyle(fontSize: 16),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
height: 45.0,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: TabBar(
|
||||
controller: tabController,
|
||||
tabAlignment: TabAlignment.start,
|
||||
isScrollable: true,
|
||||
padding: EdgeInsets.only(left: 0),
|
||||
indicatorPadding: EdgeInsets.zero,
|
||||
labelPadding: EdgeInsets.symmetric(horizontal: 10.0),
|
||||
tabs: tabList
|
||||
.map((item) => Container(
|
||||
constraints: BoxConstraints(minWidth: 70),
|
||||
alignment: Alignment.center,
|
||||
child: Badge.count(
|
||||
backgroundColor: Colors.red,
|
||||
offset: Offset(14, -4),
|
||||
count: item['badge'] ?? 0,
|
||||
isLabelVisible: item['badge'] != null && item['badge'] > 0,
|
||||
child: Text(
|
||||
item['name'],
|
||||
style: TextStyle(fontSize: 16),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
||||
unselectedLabelColor: Colors.black87,
|
||||
labelColor: Color(0xFFFF5000),
|
||||
indicator: UnderlineTabIndicator(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
borderSide: BorderSide(color: Color(0xFFFF5000), width: 2.0),
|
||||
),
|
||||
indicatorSize: TabBarIndicatorSize.label,
|
||||
unselectedLabelStyle: TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei'),
|
||||
labelStyle: TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei', fontWeight: FontWeight.w700),
|
||||
dividerHeight: 0,
|
||||
),
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
||||
unselectedLabelColor: Colors.black87,
|
||||
labelColor: Color(0xFFFF5000),
|
||||
indicator: UnderlineTabIndicator(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
borderSide: BorderSide(color: Color(0xFFFF5000), width: 2.0),
|
||||
),
|
||||
indicatorSize: TabBarIndicatorSize.label,
|
||||
unselectedLabelStyle: TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei'),
|
||||
labelStyle: TextStyle(fontSize: 16.0, fontFamily: 'Microsoft YaHei', fontWeight: FontWeight.w700),
|
||||
dividerHeight: 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
body: TabBarView(
|
||||
controller: tabController,
|
||||
children: List.generate(tabList.length, (index) {
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
_refreshData();
|
||||
},
|
||||
child: ScrollConfiguration(
|
||||
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||
child: Container(
|
||||
color: Colors.grey[50],
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
body: SafeArea(
|
||||
bottom: true,
|
||||
child: TabBarView(
|
||||
controller: tabController,
|
||||
children: List.generate(tabList.length, (index) {
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
_refreshData();
|
||||
},
|
||||
child: ScrollConfiguration(
|
||||
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||
child: Container(
|
||||
color: Colors.grey[50],
|
||||
child: Builder(builder: (context) {
|
||||
final currentOrders = _getOrderListByIndex(index);
|
||||
final isLoading = _isLoading[index] ?? false;
|
||||
final hasMoreData = _hasMoreData[index] ?? false;
|
||||
@ -658,12 +726,12 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
return _buildOrderItem(currentOrders[itemIndex]);
|
||||
},
|
||||
);
|
||||
}
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -671,15 +739,24 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
// 根据索引获取订单列表
|
||||
List<Map<String, dynamic>> _getOrderListByIndex(int index) {
|
||||
switch (index) {
|
||||
case 0: return _allOrders;
|
||||
case 1: return _pendingPaymentOrders;
|
||||
case 2: return _pendingVerificationOrders;
|
||||
case 3: return _completedOrders;
|
||||
case 4: return _closedOrders;
|
||||
case 5: return _refundingOrders;
|
||||
case 6: return _refundedOrders;
|
||||
case 7: return _cancelledOrders;
|
||||
default: return _allOrders;
|
||||
case 0:
|
||||
return _allOrders;
|
||||
case 1:
|
||||
return _pendingPaymentOrders;
|
||||
case 2:
|
||||
return _pendingVerificationOrders;
|
||||
// case 3:
|
||||
// return _completedOrders;
|
||||
case 3:
|
||||
return _closedOrders;
|
||||
case 4:
|
||||
return _refundingOrders;
|
||||
case 5:
|
||||
return _refundedOrders;
|
||||
case 6:
|
||||
return _cancelledOrders;
|
||||
default:
|
||||
return _allOrders;
|
||||
}
|
||||
}
|
||||
|
||||
@ -707,7 +784,7 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
|
||||
}
|
||||
|
||||
Widget emptyTip() {
|
||||
return Container(
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
// 搜索
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/IM/im_service.dart' hide logger;
|
||||
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||
import 'package:loopin/IM/im_service.dart';
|
||||
import 'package:loopin/api/common_api.dart';
|
||||
import 'package:loopin/components/my_toast.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
@ -41,6 +43,9 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
||||
// 统一的高度常量
|
||||
static const double _itemCornerRadius = 8;
|
||||
|
||||
// 个人信息
|
||||
final ctl = Get.find<ImUserInfoController>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -269,6 +274,11 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
||||
|
||||
// 点击关注按钮
|
||||
onFocusBtnClick(user, index) async {
|
||||
if (ctl.userID == user['id']) {
|
||||
MyToast().tip(title: '不能关注自己');
|
||||
return;
|
||||
}
|
||||
//检测是不是自己
|
||||
final vlogerId = user['id'];
|
||||
final doIFollowVloger = user['doIFollowVloger'];
|
||||
print('是否关注此用户------------->$doIFollowVloger');
|
||||
@ -507,6 +517,7 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 视频项构建
|
||||
Widget _buildVideoItem(Map<String, dynamic> video) {
|
||||
return GestureDetector(
|
||||
@ -608,9 +619,7 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
||||
)
|
||||
: null,
|
||||
),
|
||||
child: video['avatar'] == null || video['avatar'].toString().isEmpty
|
||||
? const Icon(Icons.person, size: 10, color: Colors.grey)
|
||||
: null,
|
||||
child: video['avatar'] == null || video['avatar'].toString().isEmpty ? const Icon(Icons.person, size: 10, color: Colors.grey) : null,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
// 用户名
|
||||
@ -683,6 +692,7 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
||||
|
||||
return Container();
|
||||
}
|
||||
|
||||
// 商品Tab
|
||||
Widget _buildProductTab() {
|
||||
if (_productResults.isEmpty && !_isLoading) {
|
||||
@ -920,44 +930,80 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 45,
|
||||
height: 45,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.grey[200],
|
||||
image: user['avatar'] != null
|
||||
? DecorationImage(
|
||||
image: NetworkImage(user['avatar'].toString()),
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: null,
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
logger.w(user);
|
||||
// 检测是否自己
|
||||
if (ctl.userID.value == user['id']) {
|
||||
return;
|
||||
}
|
||||
//点击头像
|
||||
final vloggerId = user['id'];
|
||||
final result = await Get.toNamed('/vloger', arguments: {'memberId': vloggerId});
|
||||
if (result != null) {
|
||||
setState(() {
|
||||
user['doIFollowVloger'] = result['followStatus'];
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
width: 45,
|
||||
height: 45,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.grey[200],
|
||||
image: user['avatar'] != null
|
||||
? DecorationImage(
|
||||
image: NetworkImage(user['avatar'].toString()),
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
child: user['avatar'] == null ? const Icon(Icons.person, color: Colors.grey, size: 20) : null,
|
||||
),
|
||||
child: user['avatar'] == null ? const Icon(Icons.person, color: Colors.grey, size: 20) : null,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
user['nickname']?.toString() ?? '未知用户',
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () async {
|
||||
// 检测是否自己
|
||||
if (ctl.userID.value == user['id']) {
|
||||
return;
|
||||
}
|
||||
//点击内容区域
|
||||
final vloggerId = user['id'];
|
||||
final result = await Get.toNamed('/vloger', arguments: {'memberId': vloggerId});
|
||||
if (result != null) {
|
||||
setState(() {
|
||||
user['doIFollowVloger'] = result['followStatus'];
|
||||
});
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
user['nickname']?.toString() ?? '未知用户',
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'粉丝: ${user['fansCount']?.toString() ?? '0'}',
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
color: Colors.grey,
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'粉丝: ${user['fansCount']?.toString() ?? '0'}',
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// 关注按钮
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
await onFocusBtnClick(user, index);
|
||||
@ -603,8 +603,15 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
|
||||
AnimatedOpacity(
|
||||
opacity: position > Duration(milliseconds: 100) ? 0.0 : 1.0,
|
||||
duration: Duration(milliseconds: 50),
|
||||
child: Image.network(
|
||||
videoData['firstFrameImg'] ?? 'https://wuzhongjie.com.cn/download/logo.png',
|
||||
// child: Image.network(
|
||||
// videoData['firstFrameImg'] ?? 'https://wuzhongjie.com.cn/download/logo.png',
|
||||
// fit: isHorizontal ? BoxFit.contain : BoxFit.cover,
|
||||
// width: double.infinity,
|
||||
// height: double.infinity,
|
||||
// ),
|
||||
child: NetworkOrAssetImage(
|
||||
imageUrl: videoData['firstFrameImg'] ?? '',
|
||||
placeholderAsset: 'assets/images/bk.jpg',
|
||||
fit: isHorizontal ? BoxFit.contain : BoxFit.cover,
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
|
||||
@ -8,7 +8,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:loopin/utils/common.dart';
|
||||
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||
import 'package:loopin/IM/im_core.dart';
|
||||
@ -21,6 +20,7 @@ import 'package:loopin/models/conversation_type.dart';
|
||||
import 'package:loopin/models/share_type.dart';
|
||||
import 'package:loopin/models/summary_type.dart';
|
||||
import 'package:loopin/service/http.dart';
|
||||
import 'package:loopin/utils/common.dart';
|
||||
import 'package:loopin/utils/download_video.dart';
|
||||
import 'package:loopin/utils/permissions.dart';
|
||||
import 'package:loopin/utils/wxsdk.dart';
|
||||
@ -298,8 +298,8 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
|
||||
|
||||
final comment = commentList[index];
|
||||
final hasReplies = comment['childCount'] > 0;
|
||||
final isExpanded = expandedReplies[comment['id']] == true;
|
||||
final replies = replyData[comment['id']] ?? [];
|
||||
final isExpanded = expandedReplies[comment['commentId']] == true;
|
||||
final replies = replyData[comment['commentId']] ?? [];
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
@ -328,9 +328,12 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
|
||||
),
|
||||
SizedBox(width: 20.0),
|
||||
GestureDetector(
|
||||
//回复评论
|
||||
onTap: () {
|
||||
setState(() {
|
||||
replyingCommentId = comment['id'];
|
||||
logger.e(comment);
|
||||
// replyingCommentId = comment['id']; //null
|
||||
replyingCommentId = comment['commentId'];
|
||||
replyingCommentUser = comment['commentUserNickname'] ?? '未知用户';
|
||||
});
|
||||
},
|
||||
@ -357,11 +360,13 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
|
||||
if (hasReplies)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
logger.e(comment);
|
||||
setState(() {
|
||||
expandedReplies[comment['id']] = !isExpanded;
|
||||
if (expandedReplies[comment['id']] == true &&
|
||||
(replyData[comment['id']] == null || replyData[comment['id']]!.isEmpty)) {
|
||||
fetchReplies(comment['id'], false);
|
||||
logger.e(replyData);
|
||||
expandedReplies[comment['commentId']] = !isExpanded;
|
||||
if (expandedReplies[comment['commentId']] == true &&
|
||||
(replyData[comment['commentId']] == null || replyData[comment['commentId']]!.isEmpty)) {
|
||||
fetchReplies(comment['commentId'], false);
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -570,7 +575,7 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
|
||||
final List<StreamSubscription> subscriptions = [];
|
||||
// 进度条slider当前阈值
|
||||
double sliderValue = 0.0;
|
||||
double sliderValue = 0.1;
|
||||
bool sliderDraging = false;
|
||||
late Duration position = Duration.zero; // 当前时长
|
||||
late Duration duration = Duration.zero; // 总时长
|
||||
@ -734,6 +739,7 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
final resCode = res['code'];
|
||||
if (resCode == 200) {
|
||||
item['doILikeThisVlog'] = !item['doILikeThisVlog'];
|
||||
setState(() {});
|
||||
}
|
||||
} catch (e) {
|
||||
logger.i('点击取消喜欢按钮报错: $e');
|
||||
@ -747,6 +753,7 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
final resCode = res['code'];
|
||||
if (resCode == 200) {
|
||||
item['doILikeThisVlog'] = !item['doILikeThisVlog'];
|
||||
setState(() {});
|
||||
}
|
||||
logger.i('点赞返回信息----------->: $res');
|
||||
} catch (e) {
|
||||
@ -770,7 +777,8 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
),
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return CommentBottomSheet(
|
||||
return SafeArea(
|
||||
child: CommentBottomSheet(
|
||||
videoId: videoId,
|
||||
onCommentCountChanged: (newCount) {
|
||||
// 更新对应视频的评论数量
|
||||
@ -779,7 +787,9 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
videoList[index]['commentsCounts'] = newCount;
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -1132,9 +1142,14 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
width: 48.0,
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
//跳转前先检测是不是自己
|
||||
final vloggerId = videoList[videoModuleController.videoPlayIndex.value]['memberId'];
|
||||
final myID = Get.find<ImUserInfoController>().userID.value;
|
||||
if (myID == vloggerId) {
|
||||
return;
|
||||
}
|
||||
player.pause();
|
||||
// 跳转到 Vloger 页面并等待返回结果
|
||||
final vloggerId = videoList[videoModuleController.videoPlayIndex.value]['memberId'];
|
||||
final result = await Get.toNamed('/vloger', arguments: {'memberId': vloggerId});
|
||||
if (result != null) {
|
||||
// 处理返回的参数
|
||||
@ -1180,10 +1195,16 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
),
|
||||
onTap: () async {
|
||||
if (!Common.isLogin()) {
|
||||
Get.toNamed('/login');
|
||||
return;
|
||||
Get.toNamed('/login');
|
||||
return;
|
||||
}
|
||||
final vlogerId = videoList[index]['memberId'];
|
||||
//不能关注自己
|
||||
final myID = Get.find<ImUserInfoController>().userID.value;
|
||||
if (myID == vlogerId) {
|
||||
MyToast().tip(title: '不能关注自己');
|
||||
return;
|
||||
}
|
||||
final doIFollowVloger = videoList[index]['doIFollowVloger'];
|
||||
// 未关注点击才去关注
|
||||
if (doIFollowVloger == false) {
|
||||
@ -1217,8 +1238,8 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
onTap: () {
|
||||
logger.d('点击了点赞按钮${videoList[index]['doILikeThisVlog']}');
|
||||
if (!Common.isLogin()) {
|
||||
Get.toNamed('/login');
|
||||
return;
|
||||
Get.toNamed('/login');
|
||||
return;
|
||||
}
|
||||
if (videoList[index]['doILikeThisVlog'] == true) {
|
||||
logger.d('点击了点赞按钮${videoList[index]['doILikeThisVlog']}');
|
||||
@ -1245,8 +1266,8 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
),
|
||||
onTap: () {
|
||||
if (!Common.isLogin()) {
|
||||
Get.toNamed('/login');
|
||||
return;
|
||||
Get.toNamed('/login');
|
||||
return;
|
||||
}
|
||||
handleComment(index);
|
||||
},
|
||||
@ -1264,8 +1285,8 @@ class _RecommendModuleState extends State<RecommendModule> {
|
||||
),
|
||||
onTap: () {
|
||||
if (!Common.isLogin()) {
|
||||
Get.toNamed('/login');
|
||||
return;
|
||||
Get.toNamed('/login');
|
||||
return;
|
||||
}
|
||||
handleShare(index);
|
||||
},
|
||||
|
||||
@ -10,7 +10,7 @@ import 'package:loopin/pages/chat/chat_no_friend.dart';
|
||||
import 'package:loopin/pages/chat/notify/interaction.dart';
|
||||
import 'package:loopin/pages/chat/notify/newFoucs.dart';
|
||||
import 'package:loopin/pages/chat/notify/noFriend.dart';
|
||||
import 'package:loopin/pages/chat/notify/system.dart';
|
||||
import 'package:loopin/pages/chat/notify/orderNotify.dart';
|
||||
import 'package:loopin/pages/groupChat/groupList.dart';
|
||||
import 'package:loopin/pages/groupChat/index.dart';
|
||||
import 'package:loopin/pages/my/all_function.dart';
|
||||
@ -27,7 +27,7 @@ import 'package:loopin/pages/my/vloger.dart';
|
||||
import 'package:loopin/pages/order/my_order.dart';
|
||||
import 'package:loopin/pages/order/seller_order.dart';
|
||||
import 'package:loopin/pages/search/index.dart';
|
||||
import 'package:loopin/pages/search/search-result.dart';
|
||||
import 'package:loopin/pages/search/search_result.dart';
|
||||
import 'package:loopin/pages/video/commonVideo.dart';
|
||||
import 'package:loopin/pages/video/report.dart';
|
||||
|
||||
@ -37,8 +37,6 @@ import '../pages/auth/login.dart';
|
||||
// 商品详细
|
||||
import '../pages/goods/detail.dart';
|
||||
import '../pages/order/detail.dart';
|
||||
// 订单
|
||||
import '../pages/order/index.dart';
|
||||
import '../pages/order/seller_detail.dart';
|
||||
// 引入工具类
|
||||
import '../utils/common.dart';
|
||||
@ -49,7 +47,7 @@ final Map<String, Widget> routes = {
|
||||
'/goods': const Goods(),
|
||||
// '/chatNoFriend': const ChatNoFriend(),
|
||||
// '/chatGroup': const ChatGroup(),
|
||||
'/order': const Order(),
|
||||
// '/order': const Order(),
|
||||
'/sellerOrder': const SellerOrder(),
|
||||
'/sellerOrder/detail': const SellerOrderDetail(),
|
||||
'/myOrder': const MyOrder(),
|
||||
@ -73,9 +71,11 @@ final Map<String, Widget> routes = {
|
||||
|
||||
//通知相关
|
||||
'/noFriend': const Nofriend(),
|
||||
'/newFoucs': const Newfoucs(),
|
||||
'/system': const System(),
|
||||
'/newFocus': const Newfoucs(),
|
||||
// '/system': const System(),
|
||||
'/interaction': const Interaction(),
|
||||
'/order': const OrderNotify(),
|
||||
|
||||
//关系链
|
||||
'/fans': const Fans(),
|
||||
'/flow': const Flowing(),
|
||||
|
||||
@ -12,10 +12,10 @@ class HttpConfig {
|
||||
// baseUrl: 'http://111.62.22.190:8080',
|
||||
// baseUrl: 'http://cjh.wuzhongjie.com.cn',
|
||||
// baseUrl: 'http://82.156.121.2:8880',
|
||||
// baseUrl: 'https://www.wuzhongjie.com.cn/prod-api',
|
||||
baseUrl: 'https://www.wuzhongjie.com.cn/prod-api',
|
||||
|
||||
// baseUrl: 'http://192.168.1.65:8880',
|
||||
baseUrl: 'http://192.168.1.22:8080',
|
||||
// baseUrl: 'http://192.168.1.22:8080',
|
||||
|
||||
// connectTimeout: Duration(seconds: 30),
|
||||
// receiveTimeout: Duration(seconds: 30),
|
||||
@ -89,9 +89,11 @@ class HttpConfig {
|
||||
// 其他异常
|
||||
if (data['code'] != 200) {
|
||||
Get.snackbar(
|
||||
'错误码${data['code']}',
|
||||
'${response.requestOptions.uri}\n${response.requestOptions.data}\n${data['msg']}' ?? '请求失败',
|
||||
duration: Duration(minutes: 1),
|
||||
// '错误码${data['code']}',
|
||||
'提示!',
|
||||
// '${response.requestOptions.uri}\n${response.requestOptions.data}\n${data['msg']}' ?? '请求失败',
|
||||
'${data['msg']}' ?? '请求失败',
|
||||
duration: Duration(seconds: 5),
|
||||
backgroundColor: Colors.red.withAlpha(230),
|
||||
colorText: Colors.white,
|
||||
icon: const Icon(Icons.error_outline, color: Colors.white),
|
||||
|
||||
56
lib/utils/deep_link_listener.dart
Normal file
@ -0,0 +1,56 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:app_links/app_links.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:loopin/IM/im_friend_listeners.dart';
|
||||
|
||||
/// 全局监听schem启动参数 wuzhongjie://open?xxx=xxx
|
||||
class DeepLinkListener extends StatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
const DeepLinkListener({required this.child, super.key});
|
||||
|
||||
@override
|
||||
State<DeepLinkListener> createState() => _DeepLinkListenerState();
|
||||
}
|
||||
|
||||
class _DeepLinkListenerState extends State<DeepLinkListener> {
|
||||
final AppLinks _appLinks = AppLinks();
|
||||
StreamSubscription? _sub;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_handleLinks();
|
||||
}
|
||||
|
||||
/// 统一处理冷启动和热启动
|
||||
void _handleLinks() {
|
||||
_sub = _appLinks.uriLinkStream.listen((Uri? uri) {
|
||||
if (uri != null) {
|
||||
_parseAppParameter(uri);
|
||||
}
|
||||
}, onError: (err) {
|
||||
logger.e('启动参数监听报错: $err');
|
||||
});
|
||||
}
|
||||
|
||||
// 小程序返回热启动参数
|
||||
void _parseAppParameter(Uri uri) {
|
||||
final appParameter = uri.queryParameters['appParameter'];
|
||||
if (appParameter != null) {
|
||||
logger.e('收到启动参数 appParameter: $appParameter');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_sub?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.child;
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
/// 精选推荐模块
|
||||
library;
|
||||
import 'dart:io';
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
// 下载管理器类
|
||||
class DownloadManager {
|
||||
|
||||
@ -186,8 +186,7 @@ String _parseCustomMessage(V2TimMessage? msg) {
|
||||
// final sum = jsonDecode(msg.cloudCustomData!);
|
||||
final elment = msg.customElem; // 所有服务端发送的通知消息都走【自定义消息类型】
|
||||
// logger.w('解析自定义消息:$sum,自定义属性:${msg.cloudCustomData}');
|
||||
logger.w(sum);
|
||||
logger.w('解析element:${elment?.desc ?? 'summary_error'}');
|
||||
logger.w('解析element:aciton=$sum,${elment?.desc ?? 'summary_error'}');
|
||||
try {
|
||||
switch (sum) {
|
||||
case SummaryType.hongbao:
|
||||
@ -275,22 +274,28 @@ String _parseCustomMessage(V2TimMessage? msg) {
|
||||
/// 回复的评论内容[comment]
|
||||
return elment!.desc!;
|
||||
case NotifyMessageTypeConstants.orderRecharge:
|
||||
// 充值成功通知
|
||||
// 充值成功通知,
|
||||
/// 订单编号[orderID]
|
||||
/// 充值金额[amount]
|
||||
/// 充值后帐户余额[totalAmount]
|
||||
return elment!.desc!;
|
||||
case NotifyMessageTypeConstants.orderPay:
|
||||
// 订单交易结果通知(商品购买成功后)
|
||||
// 订单交易结果通知(商品购买成功后--下单成功)
|
||||
/// 订单编号[orderID]
|
||||
/// 订单金额[amount]
|
||||
/// 商品名称[name]
|
||||
/// 商品描述[describe]
|
||||
/// 商品价格[price]
|
||||
/// 商品主图[pic]
|
||||
return elment!.desc!;
|
||||
case NotifyMessageTypeConstants.orderWithDraw:
|
||||
// 提现结果通知(成功/失败-----提现成功/提现失败)
|
||||
/// 提现状态[status] 0=失败 1=成功
|
||||
|
||||
return elment!.desc!;
|
||||
case NotifyMessageTypeConstants.orderRefund:
|
||||
// 订单退款通知
|
||||
/// 退款状态[status] 0=失败 1=成功
|
||||
/// 订单编号[orderID]
|
||||
/// 订单金额[amount]
|
||||
/// 商品名称[name]
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
// 扫码 类型
|
||||
class QrTypeCode {
|
||||
static const String hxm = 'hxm-';
|
||||
static const String hxm = 'HXM-';
|
||||
static const String hym = 'hym-';
|
||||
static const String tgm = 'tgm-';
|
||||
}
|
||||
|
||||
enum ScanCodeType {
|
||||
verification('hxm'), // 核销码
|
||||
verification('HXM'), // 核销码
|
||||
friend('hym'), // 好友码
|
||||
promotion('tgm'); // 推广码
|
||||
|
||||
|
||||
19
lib/utils/wechat_business_view.dart
Normal file
@ -0,0 +1,19 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:loopin/IM/im_friend_listeners.dart';
|
||||
|
||||
class WechatBusinessView {
|
||||
static const MethodChannel _channel = MethodChannel('wechat_business_view'); // mainactivity.kt,appdelegate.swift
|
||||
|
||||
/// 调起微信确认收款码
|
||||
static Future<bool> openBusinessView({required String packageInfo}) async {
|
||||
try {
|
||||
final result = await _channel.invokeMethod('openBusinessView', {
|
||||
'package': packageInfo,
|
||||
});
|
||||
return result == true;
|
||||
} on PlatformException catch (e) {
|
||||
logger.w('调用失败: ${e.message}');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -43,6 +43,7 @@ class Wxsdk {
|
||||
"clientId": "428a8310cd442757ae699df5d894f051",
|
||||
"grantType": "social"
|
||||
});
|
||||
logger.e(serverRes);
|
||||
final info = Get.find<ImUserInfoController>();
|
||||
info.customInfo['openId'] = serverRes['data']['openId'];
|
||||
info.updateOpenId();
|
||||
@ -63,6 +64,8 @@ class Wxsdk {
|
||||
final ctl = Get.find<BalanceController>();
|
||||
// 刷新记录
|
||||
ctl.getData(reset: true);
|
||||
// 刷新钱包
|
||||
ctl.getAccount();
|
||||
} else {
|
||||
if (res.errCode == -2) {
|
||||
logger.w("用户取消支付");
|
||||
@ -156,7 +159,7 @@ class Wxsdk {
|
||||
var miniProgram = MiniProgram(
|
||||
username: "gh_2ffaecc5508e", // 小程序原始ID
|
||||
path: "/pages/index/index?id=$orderId", // 打开时带的路径参数
|
||||
miniProgramType: WXMiniProgramType.preview,
|
||||
miniProgramType: WXMiniProgramType.preview, // 预览模式,正式环境更换release
|
||||
);
|
||||
Fluwx().open(target: miniProgram);
|
||||
}
|
||||
|
||||
76
pubspec.lock
@ -17,6 +17,38 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
app_links:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: app_links
|
||||
sha256: "5f88447519add627fe1cbcab4fd1da3d4fed15b9baf29f28b22535c95ecee3e8"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "6.4.1"
|
||||
app_links_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: app_links_linux
|
||||
sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
app_links_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: app_links_platform_interface
|
||||
sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
app_links_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: app_links_web
|
||||
sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -529,10 +561,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: fluwx
|
||||
sha256: "9db31d54043363c9c8283b5f0bc4df982edb45ba19d800df9d7de96a205371ae"
|
||||
sha256: "9bac596b34f37b08fd4cae0d0e1e205d0dc395dbec4200c588eed9b4b446f69f"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "5.7.0"
|
||||
version: "5.7.2"
|
||||
form_builder_validators:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -605,6 +637,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
gtk:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: gtk
|
||||
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
html:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1682,6 +1722,38 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
webview_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: webview_flutter
|
||||
sha256: c3e4fe614b1c814950ad07186007eff2f2e5dd2935eba7b9a9a1af8e5885f1ba
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.13.0"
|
||||
webview_flutter_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_android
|
||||
sha256: "9a25f6b4313978ba1c2cda03a242eea17848174912cfb4d2d8ee84a556f248e3"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.10.1"
|
||||
webview_flutter_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_platform_interface
|
||||
sha256: "63d26ee3aca7256a83ccb576a50272edd7cfc80573a4305caa98985feb493ee0"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.14.0"
|
||||
webview_flutter_wkwebview:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_wkwebview
|
||||
sha256: fb46db8216131a3e55bcf44040ca808423539bc6732e7ed34fb6d8044e3d512f
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.23.0"
|
||||
wechat_assets_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
25
pubspec.yaml
@ -85,7 +85,8 @@ dependencies:
|
||||
city_pickers: ^1.3.0
|
||||
bottom_picker: ^3.2.1
|
||||
|
||||
fluwx: ^5.7.0 #微信sdk
|
||||
# fluwx: ^5.7.0 #微信sdk
|
||||
fluwx: ^5.7.2 #微信sdk
|
||||
flutter_image_compress: ^2.4.0 #处理图片
|
||||
video_thumbnail: ^0.5.6 #视频首帧截取
|
||||
record: ^6.0.0 #音频
|
||||
@ -100,6 +101,8 @@ dependencies:
|
||||
image_cropper: ^9.1.0
|
||||
pretty_qr_code: ^3.5.0
|
||||
lottie: ^3.3.1
|
||||
webview_flutter: ^4.13.0
|
||||
app_links: ^6.4.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_launcher_icons: ^0.13.1 # 使用最新版本
|
||||
@ -188,21 +191,23 @@ flutter_launcher_icons:
|
||||
|
||||
|
||||
#dart run flutter_native_splash:create
|
||||
#flutter pub run flutter_native_splash:create
|
||||
#dart run flutter_native_splash:remove
|
||||
flutter_native_splash:
|
||||
ios: true
|
||||
android: true
|
||||
color: "#000000"
|
||||
image: assets/images/logo/start.png
|
||||
color: "#ffffff"
|
||||
# image: assets/images/logo/start.png
|
||||
fullscreen: true
|
||||
# background_image: assets/images/logo/start1.png
|
||||
# color_dark: "#000000"
|
||||
# image_dark: assets/images/logo/start1.png
|
||||
background_image: assets/images/logo/start.png
|
||||
# color_dark: "#ffffff"
|
||||
# image_dark: assets/images/logo/start.png
|
||||
|
||||
android_12:
|
||||
image: assets/images/logo/logo.png
|
||||
image: assets/images/logo/androidlogo.png
|
||||
icon_background_color: "#000000"
|
||||
# image_dark: assets/images/logo/start.png
|
||||
# icon_background_color_dark: "#000000"
|
||||
# background_image: assets/images/logo/start.png
|
||||
|
||||
image_dark: assets/images/logo/androidlogo.png
|
||||
icon_background_color_dark: "#000000"
|
||||
|
||||
web: false
|
||||