Compare commits

...

11 Commits
410 ... master

Author SHA1 Message Date
abu
67ed131eeb fix_个人中心&博主主页 2025-10-13 22:52:47 +08:00
abu
314aeddf23 fix_朋友&关注 2025-10-13 13:17:36 +08:00
abu
9902980bf8 fix_视频评论安全区域 2025-10-11 18:22:44 +08:00
abu
0fd1ed8799 remove_android 2025-10-11 11:47:58 +08:00
abu
fa619318b8 android_for_mac 2025-10-11 11:43:53 +08:00
abu
c104e9a627 except Android 2025-10-11 11:29:29 +08:00
abu
86f5234060 fix_4.14 2025-10-10 11:44:34 +08:00
abu
f9a1f2ff9f 4.1.1 2025-10-10 11:44:34 +08:00
abu
4c15dee33a start_page 2025-10-10 11:44:34 +08:00
abu
564cd38516 410 2025-10-10 11:44:34 +08:00
Seven Tsui
6291dbc55d 首页3个tab视频快速切换index边界问题修复 2025-09-22 15:16:40 +08:00
95 changed files with 6760 additions and 3522 deletions

BIN
android_macos.zip Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1759238468389" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5738" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M256 512m-74.666667 0a74.666667 74.666667 0 1 0 149.333334 0 74.666667 74.666667 0 1 0-149.333334 0Z" fill="#111111" p-id="5739"></path><path d="M512 512m-74.666667 0a74.666667 74.666667 0 1 0 149.333334 0 74.666667 74.666667 0 1 0-149.333334 0Z" fill="#111111" p-id="5740"></path><path d="M768 512m-74.666667 0a74.666667 74.666667 0 1 0 149.333334 0 74.666667 74.666667 0 1 0-149.333334 0Z" fill="#111111" p-id="5741"></path></svg>

After

Width:  |  Height:  |  Size: 765 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -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

View File

@ -1,7 +1,11 @@
PODS:
- app_links (6.4.1):
- Flutter
- audioplayers_darwin (0.0.1):
- Flutter
- FlutterMacOS
- connectivity_plus (0.0.1):
- Flutter
- device_info_plus (0.0.1):
- Flutter
- Flutter (1.0.0)
@ -19,7 +23,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 +37,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 +71,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 +91,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,10 +104,15 @@ 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`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- Flutter (from `Flutter`)
- flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`)
@ -131,6 +140,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,8 +155,12 @@ SPEC REPOS:
- WechatOpenSDK-XCFramework
EXTERNAL SOURCES:
app_links:
:path: ".symlinks/plugins/app_links/ios"
audioplayers_darwin:
:path: ".symlinks/plugins/audioplayers_darwin/darwin"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
Flutter:
@ -201,22 +215,26 @@ 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
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
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 +244,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

View File

@ -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;

View File

@ -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 deprecatedplease use the new field businessID below.
@objc func offlinePushCertificateID() -> Int32 {
return TencentCloudChatPushFlutterModal.shared.offlinePushCertificateID();

View File

@ -2,16 +2,17 @@
"images" : [
{
"filename" : "background.png",
"idiom" : "universal",
"scale" : "1x"
"idiom" : "universal"
},
{
"idiom" : "universal",
"scale" : "2x"
},
"appearances" : [
{
"idiom" : "universal",
"scale" : "3x"
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "darkbackground.png",
"idiom" : "universal"
}
],
"info" : {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 B

After

Width:  |  Height:  |  Size: 69 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

View File

@ -5,15 +5,48 @@
"idiom" : "universal",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "LaunchImageDark.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "LaunchImage@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "LaunchImageDark@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "LaunchImage@3x.png",
"idiom" : "universal",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "LaunchImageDark@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 536 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -17,7 +17,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleAspectFill" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
@ -38,7 +38,7 @@
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="1024" height="1536"/>
<image name="LaunchImage" width="750" height="1334"/>
<image name="LaunchBackground" width="1" height="1"/>
</resources>
</document>

View File

@ -1,7 +1,7 @@
<?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>
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
@ -34,6 +34,16 @@
<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>
@ -49,7 +59,15 @@
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsArbitraryLoadsForMedia</key>
<true/>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string>App 需要访问您的位置以提供附近推荐内容和标记上传视频的位置</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>App 需要访问您的位置以提供个性化内容和附近服务。</string>
<key>NSCameraUsageDescription</key>
<string>App需要使用您的相机进行拍摄</string>
<key>NSMicrophoneUsageDescription</key>
@ -62,8 +80,19 @@
<string>App需要访问您的相册用于选择图片或视频</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>zh-Hans</string>
</array>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen.storyboard</string>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIStatusBarHidden</key>
@ -83,15 +112,15 @@
</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>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>weixin</string>
<string>weixinULAPI</string>
<string>weixinURLParamsAPI</string>
</array>
</dict>
</plist>

View File

@ -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();

View File

@ -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;

View File

@ -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 == '/';
},
);
}
}

View File

@ -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);
}
}
}
}
}
//

View File

@ -47,6 +47,11 @@ class IMMessage {
title: myInfo.nickname.value,
desc: parseMessageSummary(msg),
ext: jsonEncode({"userID": myInfo.userID.value, "title": myInfo.nickname.value}),
//ios ios>18
iOSPushType: 0, //0= 1=/
iOSSound: 'default', // kIOSOfflinePushDefaultSound
ignoreIOSBadge: false, //
iOSInterruptionLevel: 'active', // active // time-sensitive
);
sendRes = await TencentImSDKPlugin.v2TIMManager.getMessageManager().sendMessage(
message: msg,
@ -75,7 +80,12 @@ class IMMessage {
OfflinePushInfo offlinePushInfo = OfflinePushInfo(
title: groupName,
desc: parseMessageSummary(msg),
ext: jsonEncode({"groupID": groupID, "title": groupName ?? ''}),
ext: jsonEncode({"groupID": groupID, "title": groupName ?? '群聊'}),
//ios ios>18
iOSPushType: 0, //0= 1=/
iOSSound: 'default', // kIOSOfflinePushDefaultSound
ignoreIOSBadge: false, //
iOSInterruptionLevel: 'active', //
);
sendRes = await TencentImSDKPlugin.v2TIMManager.getMessageManager().sendMessage(
message: msg,

View File

@ -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);
@ -124,6 +124,15 @@ class ImService {
);
} else {
logger.i("IM 登录失败:${result.code} - ${result.desc}");
if (result.code == 70013) {
Get.snackbar(
'登录失败',
'请求的 Identifier 与生成 UserSig 的 Identifier 不匹配',
backgroundColor: Colors.red.withAlpha(230),
colorText: Colors.white,
icon: const Icon(Icons.error_outline, color: Colors.white),
);
} else {
Get.snackbar(
'登录失败',
'${result.code} - ${result.desc}',
@ -132,6 +141,7 @@ class ImService {
icon: const Icon(Icons.error_outline, color: Colors.white),
);
}
}
return result;
}
@ -275,7 +285,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 +307,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 +514,14 @@ class ImService {
);
}
///
Future<ImResult> markC2CMessageAsReadResult({
required String userID,
}) async {
final res = await TIMMessageManager.instance.markC2CMessageAsRead(userID: userID);
return ImResult.wrapNoData(res);
}
///
///
///
@ -646,6 +664,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();

View File

@ -48,20 +48,25 @@ class PushService {
}
//
// if (Platform.isAndroid) {
if (Platform.isAndroid) {
// await compute(_registerPushInIsolate, {
// 'sdkAppId': sdkAppId,
// 'appKey': appKey,
// 'apnsCertificateID': apnsCertificateID,
// });
// } else {
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,10 @@ class PushService {
if (Platform.isAndroid) {
await TencentImSDKPlugin.v2TIMManager.login(userID: Storage.read('userId'), userSig: Storage.read('userSig'));
}
logger.i('推送服务已注册,手机:$devices,证书ID:$apnsCertificateID');
// final token = await TencentCloudChatPush().getAndroidPushToken();
logger.i(
'推送服务已注册,手机:$devices,证书ID:$apnsCertificateID',
);
// 线
_addPushListener();
@ -123,19 +130,23 @@ class PushService {
///
static void _handleNotificationClick(String ext, {String? userID, String? groupID}) async {
try {
// ext={id:ID,type:'newFocus',userID:idgroupIDID}
// ext={id:ID,type:'newFocus',userID:idgroupIDID} // type=
// final ext = jsonEncode({
// "userID": "123456",
// "groupID": "654321",
// });
final data = jsonDecode(ext);
logger.i(data);
final type = data['type'];
final router = conversationTypeFromString(type);
String extGroupID = data['groupID'] ?? '';
final isGroup = extGroupID.isNotEmpty;
// final type = data['type'];
final router = conversationTypeFromString(data['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 +171,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");

View File

@ -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';
}

View File

@ -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'; //
}

View File

@ -50,7 +50,8 @@ class _ImageViewerState extends State<ImageViewer> {
Widget build(BuildContext context) {
var imgCount = widget.images?.length;
return Scaffold(
return SafeArea(
child: Scaffold(
body: Stack(
children: [
Positioned(
@ -113,6 +114,7 @@ class _ImageViewerState extends State<ImageViewer> {
),
],
),
),
);
}
}

View File

@ -27,12 +27,14 @@ class NetworkOrAssetImage extends StatelessWidget {
width: width,
height: height,
fit: fit,
placeholder: (context, url) => Image.asset(
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,

View 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),
);
}
}

View File

@ -5,6 +5,8 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:loopin/IM/controller/tab_bar_controller.dart';
import 'package:loopin/models/tab_type.dart';
import 'package:loopin/pages/video/module/attention.dart';
import 'package:loopin/pages/video/module/friend.dart';
import 'package:loopin/pages/video/module/recommend.dart';
import 'package:loopin/update/upgrade_service.dart';
import 'package:loopin/utils/common.dart';
@ -32,15 +34,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 +128,62 @@ class _LayoutState extends State<Layout> {
return Scaffold(
backgroundColor: Colors.grey[50],
// body: pageList[pageCurrent],
body: Obx(() {
return Stack(
// 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: [
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(
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底部导航栏的水波纹
@ -222,8 +252,16 @@ class _LayoutState extends State<Layout> {
if (videoModuleController.videoTabIndex.value == 2) {
RecommendModule.playVideo();
}
if (videoModuleController.videoTabIndex.value == 1) {
FriendModule.playVideo();
}
if (videoModuleController.videoTabIndex.value == 0) {
AttentionModule.playVideo();
}
} else {
RecommendModule.pauseVideo();
FriendModule.pauseVideo();
AttentionModule.pauseVideo();
}
if ([2, 3, 4].contains(index) && !Common.isLogin()) {
Get.toNamed('/login');
@ -241,6 +279,6 @@ class _LayoutState extends State<Layout> {
myPageKey.currentState?.refreshData();
}
videoModuleController.updateLayoutPage(index);
setState(() {});
// setState(() {});
}
}

View File

@ -7,13 +7,15 @@ 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/network_utils.dart';
import 'package:loopin/utils/storage.dart';
import 'package:media_kit/media_kit.dart';
import 'package:shirne_dialog/shirne_dialog.dart';
@ -24,18 +26,21 @@ import 'layouts/index.dart';
import 'router/index.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
//
NetworkUtils().initialize();
// 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,7 +91,8 @@ class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
return DeepLinkListener(
child: GetMaterialApp(
title: '无终街',
locale: const Locale('zh'),
supportedLocales: const [
@ -115,6 +121,7 @@ class App extends StatelessWidget {
Get.put<RouteObserver<PageRoute>>(routeObserver);
return child!;
},
),
);
}
}

View File

@ -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':

View File

@ -7,7 +7,7 @@ extension ShareTypeExtension on ShareType {
case ShareType.video:
return 'https://wuzhongjie.com.cn/spa/video-detail';
case ShareType.shop:
return 'https://wuzhongjie.com.cn/spa/goods-derail';
return 'https://wuzhongjie.com.cn/spa/goods-detail';
}
}
}

View File

@ -9,8 +9,11 @@ import 'package:get/get.dart';
import 'package:loopin/IM/im_core.dart' as im_core;
import 'package:loopin/IM/im_service.dart';
import 'package:loopin/api/common_api.dart';
import 'package:loopin/components/my_toast.dart';
import 'package:loopin/components/web_page.dart';
import 'package:loopin/controller/video_module_controller.dart';
import 'package:loopin/service/http.dart';
import 'package:loopin/styles/index.dart';
import 'package:loopin/utils/storage.dart';
import 'package:shirne_dialog/shirne_dialog.dart';
@ -25,12 +28,12 @@ class Login extends StatefulWidget {
class _LoginState extends State<Login> {
final Map authObj = {'phonenumber': '', 'smsCode': '', 'clientId': '428a8310cd442757ae699df5d894f051', 'grantType': 'sms'};
bool isChecked = false;
final fieldController = TextEditingController();
Timer? timer;
String vcodeText = '获取验证码';
bool disabled = false;
int time = 6;
int time = 60;
@override
void initState() {
@ -54,6 +57,10 @@ class _LoginState extends State<Login> {
//
void handleSubmit() async {
FocusScope.of(context).unfocus(); //
if (!isChecked) {
MyToast().tip(title: '未勾选隐私协议');
return;
}
if (authObj['phonenumber'] == '') {
MyDialog.toast('手机号不能为空', icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
} else if (!Utils.checkTel(authObj['phonenumber'])) {
@ -112,6 +119,7 @@ class _LoginState extends State<Login> {
void handleVcode() {
logger.i(authObj);
FocusScope.of(context).unfocus(); //
if (authObj['phonenumber'] == '') {
MyDialog.toast('手机号不能为空', icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
} else if (!Utils.checkTel(authObj['phonenumber'])) {
@ -156,7 +164,8 @@ class _LoginState extends State<Login> {
forceMaterialTransparency: true,
toolbarHeight: 0,
),
body: Container(
body: SingleChildScrollView(
child: Container(
alignment: Alignment.center,
margin: const EdgeInsets.only(top: 50.0),
child: Column(
@ -299,12 +308,66 @@ class _LoginState extends State<Login> {
),
),
)),
const SizedBox(
height: 10.0,
Padding(
padding: const EdgeInsets.fromLTRB(24.0, 0.0, 24.0, 0.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end, //
children: [
Transform.scale(
scale: 0.8, // Checkbox 使
child: Checkbox(
value: isChecked,
onChanged: (value) {
setState(() {
isChecked = value ?? false;
});
},
shape: const CircleBorder(),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
activeColor: FStyle.primaryColor,
),
),
Expanded(
child: GestureDetector(
onTap: () {
logger.w(3555);
FocusScope.of(context).unfocus();
Get.to(
() => SafeArea(
child: WebPage(url: 'https://www.wuzhongjie.com.cn/download/yinsizhengce.html', title: '隐私协议'),
),
);
},
child: Text.rich(
TextSpan(
text: "未注册的手机号验证后将自动创建用户账号,登录即代表您已同意",
style: const TextStyle(
fontSize: 12,
color: Colors.grey,
height: 1.1,
),
children: [
TextSpan(
text: "《使用条款及隐私协议》",
style: const TextStyle(
fontSize: 12,
color: FStyle.primaryColor,
height: 1.2,
),
),
],
),
),
));
),
),
],
),
),
],
),
),
),
),
);
}
}

View File

@ -8,21 +8,31 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:loopin/IM/controller/chat_controller.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/animation.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/chat/components/reportChat.dart';
import 'package:loopin/service/http.dart';
import 'package:loopin/utils/audio_player_service.dart';
import 'package:loopin/utils/permissions.dart';
import 'package:loopin/utils/snapshot.dart';
import 'package:loopin/utils/voice_service.dart';
import 'package:mime/mime.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:shirne_dialog/shirne_dialog.dart';
import 'package:tencent_cloud_chat_sdk/enum/message_status.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_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';
@ -43,11 +53,14 @@ class Chat extends StatefulWidget {
class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
final ImagePicker _picker = ImagePicker();
bool hasMicPermission = false; //
late final ChatDetailController controller;
//
late final Rx<V2TimConversation> arguments;
String blackTxt = '拉黑';
late String selfUserId;
//
@ -81,7 +94,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
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'},
];
@ -157,6 +170,65 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
super.dispose();
}
//
Future<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 == arguments.value.userID) {
//
logger.e('匹配');
blackTxt = '取消拉黑';
}
}
}
}
//
void _cancelBlack() async {
logger.w('开始执行取消拉黑${arguments.value.userID}');
final res = await ImService.instance.deleteFromBlackList(userIDList: [arguments.value.userID ?? '']);
logger.w(res.success);
//
if (res.success) {
blackTxt = '拉黑';
Get.back();
}
}
//
void _addBlack() async {
logger.w('开始执行拉黑${arguments.value.userID}');
final res = await ImService.instance.addToBlackList(userIDList: [arguments.value.userID ?? '']);
logger.w(res.success);
//
if (res.success) {
blackTxt = '取消拉黑';
//退
await ImService.instance.deleteConversation(conversationID: 'c2c_${arguments.value.userID}');
final ctl = Get.find<ChatController>();
ctl.chatList.removeWhere(
(conv) => conv.conversation.conversationID == 'c2c_${arguments.value.userID}',
);
ctl.chatList.refresh();
Get.back();
Get.back();
}
}
///
void _handleBlack() {
if (blackTxt == '拉黑') {
//_addBlack
_addBlack();
} else {
_cancelBlack();
}
}
//
void setRemark() async {
String remark = '';
@ -285,6 +357,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
List<Widget> renderChatList() {
List<Widget> msgtpl = [];
for (var item in controller.chatList) {
// logger.e(item.status);
//
if (item.localCustomData == 'time_label') {
msgtpl.add(Container(
@ -322,6 +395,18 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
msgtpl.add(
RenderChatItem(
data: item,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (item.status == MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL) ...[
Icon(
Icons.error,
color: Colors.red,
size: 18,
),
SizedBox(width: 4),
],
Flexible(
child: Ink(
decoration: BoxDecoration(
color: !(item.isSelf ?? false) ? Color(0xFFFFFFFF) : Color(0xFF89E45B),
@ -332,7 +417,10 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
borderRadius: BorderRadius.circular(10.0),
child: Container(
padding: const EdgeInsets.all(10.0),
child: RichTextUtil.getRichText(item.textElem?.text ?? '', color: !(item.isSelf ?? false) ? Colors.black : Colors.white), // emoj//
child: RichTextUtil.getRichText(
item.textElem?.text ?? '',
color: !(item.isSelf ?? false) ? Colors.black : Colors.white,
), // emoj//
),
onLongPress: () {
contextMenuDialog();
@ -340,13 +428,27 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
),
),
),
],
),
),
);
}
// gif表情模板=8
else if (item.elemType == 8) {
msgtpl.add(RenderChatItem(
data: item,
child: Ink(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (item.status == MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL) ...[
Icon(
Icons.error,
color: Colors.red,
size: 18,
),
SizedBox(width: 4),
],
Ink(
child: InkWell(
overlayColor: WidgetStateProperty.all(Colors.transparent),
child: Container(
@ -362,6 +464,8 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
},
),
),
],
),
));
}
// =3
@ -371,7 +475,18 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
List<String> imagePaths = originImage != null ? [originImage.url!] : [];
msgtpl.add(RenderChatItem(
data: item,
child: Ink(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (item.status == MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL) ...[
Icon(
Icons.error,
color: Colors.red,
size: 18,
),
SizedBox(width: 4),
],
Ink(
child: InkWell(
onTap: () {
//
@ -402,7 +517,8 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
color: Colors.grey[300],
alignment: Alignment.center,
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null,
value:
loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null,
),
);
},
@ -420,6 +536,8 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
},
),
),
],
),
));
}
// =5
@ -427,7 +545,18 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
// print(item.videoElem!.toLogString());
msgtpl.add(RenderChatItem(
data: item,
child: Ink(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (item.status == MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL) ...[
Icon(
Icons.error,
color: Colors.red,
size: 18,
),
SizedBox(width: 4),
],
Ink(
child: InkWell(
overlayColor: WidgetStateProperty.all(Colors.transparent),
child: SizedBox(
@ -440,6 +569,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
child: NetworkOrAssetImage(
imageUrl: item.videoElem?.snapshotUrl ?? '',
width: 120,
placeholderAsset: 'assets/images/bk.jpg',
),
),
const Align(
@ -478,6 +608,8 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
},
),
),
],
),
));
}
// =4
@ -487,6 +619,17 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
final maxWidth = (durationSeconds / 60 * 230).clamp(80.0, 230.0);
List<Widget> audiobody = [
Row(
mainAxisSize: MainAxisSize.min,
children: [
if (item.status == MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL) ...[
Icon(
Icons.error,
color: Colors.red,
size: 18,
),
SizedBox(width: 4),
],
Ink(
decoration: BoxDecoration(
color: !(item.isSelf ?? false) ? const Color(0xFFFFFFFF) : const Color(0xFF89E45B),
@ -551,6 +694,8 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
},
),
),
],
),
const SizedBox(
width: 5.0,
),
@ -597,7 +742,18 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
// ID
Get.toNamed('/goods', arguments: {'goodsId': goodsId, 'userID': userID});
},
child: Container(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (item.status == MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL) ...[
Icon(
Icons.error,
color: Colors.red,
size: 18,
),
SizedBox(width: 4),
],
Container(
width: 160,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(15.0), boxShadow: [
@ -613,6 +769,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
NetworkOrAssetImage(
imageUrl: url,
width: 160.0,
placeholderAsset: 'assets/images/bk.jpg',
),
Container(
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
@ -658,6 +815,8 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
],
),
),
],
),
),
));
}
@ -672,9 +831,21 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
final width = obj['width'] as num;
final height = obj['height'] as num;
final isHorizontal = width > height;
msgtpl.add(RenderChatItem(
msgtpl.add(
RenderChatItem(
data: item,
child: Ink(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (item.status == MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL) ...[
Icon(
Icons.error,
color: Colors.red,
size: 18,
),
SizedBox(width: 4),
],
Ink(
child: InkWell(
overlayColor: WidgetStateProperty.all(Colors.transparent),
child: SizedBox(
@ -735,7 +906,10 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
// },
),
),
));
],
),
),
);
}
// ==2
else if (item.elemType == 2 && item.cloudCustomData == SummaryType.hongbao) {
@ -745,7 +919,18 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
// final maxNum = obj['maxNum'];
msgtpl.add(RenderChatItem(
data: item,
child: Ink(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (item.status == MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL) ...[
Icon(
Icons.error,
color: Colors.red,
size: 18,
),
SizedBox(width: 4),
],
Ink(
decoration: BoxDecoration(
color: const Color(0xFFFF7F43),
borderRadius: BorderRadius.circular(10.0),
@ -805,6 +990,8 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
},
),
),
],
),
));
}
// =7
@ -1088,7 +1275,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
}
//
Future<void> sendMessage(message) async {
Future<void> sendMessage(message, {bool? isHb}) async {
//
List<V2TimMessage> messagesToInsert = [];
V2TimMessage? lastRealMsg;
@ -1134,9 +1321,15 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
// controller.chatList.addAll(messagesToInsert);
controller.scrollToBottom();
print('发送成功');
logger.w('发送成功');
if (isHb == true) {
createHb(res.data);
}
} else {
print('消息发送失败: ${res.code} - ${res.desc}');
logger.e('消息发送失败: ${res.code} - ${res.desc}');
if (res.code == 20007) {
MyToast().tip(title: '您已被对方拉黑,无法发送消息', position: 'top');
}
}
}
@ -1210,26 +1403,69 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
//
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 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 makeJson = jsonEncode({
"amount": amount,
// "packetId": packRes['data']['packetId'],
"packetId": '',
"totalAmount": totalAmount,
"remark": remark,
"maxNum": maxNum,
"totalCount": totalCount, //
"open": false,
});
final res = await IMMessage().createCustomMessage(data: makeJson);
if (res.success && (res.data != null)) {
final custMsg = res.data!.messageInfo;
custMsg!.cloudCustomData = SummaryType.hongbao;
sendMessage(res.data!.messageInfo);
Get.back();
sendMessage(res.data!.messageInfo, isHb: true);
Navigator.pop(context);
}
}
//
void createHb(V2TimMessage msg) async {
// final makeJson = jsonEncode({
// "packetId": packRes['data']['packetId'],
// "packetId": '',
// "totalAmount": totalAmount,
// "remark": remark,
// "totalCount": totalCount, //
// "open": false,
// });
final data = jsonDecode(msg.customElem!.data!);
//
final makeData = {
"chatType": "1",
"totalAmount": data['totalAmount'],
"totalCount": data['totalCount'],
"receiverId": arguments.value.userID,
"remark": data['remark'],
};
logger.w(makeData);
final packRes = await Http.post(CommonApi.redpacket, data: makeData);
data['packetId'] = packRes['data']['packetId'];
msg.customElem!.data = jsonEncode(data);
//
await ImService.instance.modifyMessage(message: msg);
logger.e(packRes);
}
// =3
void sendImage(imgPath) async {
final resImg = await IMMessage().createImageMessage(imagePath: imgPath);
@ -1341,6 +1577,11 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
///
Future<void> _pickPhoto() async {
final hasPer = await Permissions.requestCameraPermission(title: '相机权限使用说明', content: '聊天时发送拍摄的照片');
if (!hasPer) {
Permissions.showPermissionDialog('相机');
return;
}
final XFile? photo = await _picker.pickImage(
source: ImageSource.camera,
maxWidth: 1920,
@ -1355,6 +1596,11 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
///
Future<void> _pickVideo() async {
final hasPer = await Permissions.requestCameraPermission(title: '相机权限使用说明', content: '聊天时发送拍摄的视频');
if (!hasPer) {
Permissions.showPermissionDialog('相机');
return;
}
final XFile? video = await _picker.pickVideo(
source: ImageSource.camera,
maxDuration: const Duration(minutes: 1),
@ -1394,6 +1640,12 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
////
void pickFile(BuildContext context) async {
//
final hasPer = await Permissions.requestVideoPermission();
if (!hasPer) {
Permissions.showPermissionDialog('相册');
return;
}
final pickedAssets = await AssetPicker.pickAssets(
context,
pickerConfig: AssetPickerConfig(
@ -1439,10 +1691,10 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
if (file != null) {
var fileSizeInBytes = await file.length();
var sizeInMB = fileSizeInBytes / (1024 * 1024);
if (sizeInMB > 28) {
MyDialog.toast('图片大小不能超过28MB', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
if (sizeInMB > 100) {
MyDialog.toast('文件大小不能超过100MB', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
} else {
print("图片合法,大小:$sizeInMB MB");
print("视频合法,大小:$sizeInMB MB");
//
var snapshot = await generateVideoThumbnail(file.path);
String? mimeType = await asset.mimeTypeAsync;
@ -1479,7 +1731,8 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
context: context,
builder: (context) {
final obj = jsonDecode(data.customElem!.data!);
final amount = obj['amount'];
final totalAmount = obj['totalAmount'];
final packetId = obj['packetId'];
final remark = obj['remark'];
final open = obj['open'] ?? false;
@ -1513,7 +1766,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
),
SizedBox(height: 10),
Text(
amount,
totalAmount,
style: const TextStyle(color: Color(0xFFFFF9C7), fontWeight: FontWeight.w500, fontSize: 20.0),
),
SizedBox(height: 20.0),
@ -1524,7 +1777,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
SizedBox(
height: 100.0,
),
if (open == false && data.isSelf == false)
if (open == false)
AnimatedBuilder(
animation: animTurns,
builder: (context, child) {
@ -1550,6 +1803,9 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
//
//--------
//
final ctl = Get.find<ImUserInfoController>();
try {
await Http.post(CommonApi.getpacket, data: {"packetId": packetId, "memberId": ctl.userID.value});
obj['open'] = true; //true
data.customElem!.data = jsonEncode(obj);
ImService.instance.modifyMessage(message: data);
@ -1559,6 +1815,9 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
animController.reset();
Get.back();
});
} catch (e) {
logger.e(e);
}
},
),
);
@ -1624,6 +1883,25 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
// );
}
//
// void _handleBlack() async {
// logger.w('开始执行拉黑${arguments.value.userID}');
// final res = await ImService.instance.addToBlackList(userIDList: [arguments.value.userID ?? '']);
// logger.w(res.success);
// //
// if (res.success) {
// //退
// await ImService.instance.deleteConversation(conversationID: arguments.value.conversationID);
// final ctl = Get.find<ChatController>();
// ctl.chatList.removeWhere(
// (conv) => conv.conversation.conversationID == arguments.value.conversationID,
// );
// ctl.chatList.refresh();
// Get.back();
// Get.back();
// }
// }
//
void sendRedPacketDialog() {
showModalBottomSheet(
@ -1639,7 +1917,15 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
builder: (context) {
return RedPacket(
flag: false,
onSend: (date) {
onSend: (date) async {
//
// final res = await ImService.instance.blackList();
// if (res.success && res.data != null) {
// //
// final blackList = res.data;
// blackList
// }
// ImService.instance.isMyFriend(arguments.value.userID!, FriendTypeEnum.V2TIM_FRIEND_TYPE_SINGLE);
sendHongbao(date);
});
},
@ -1674,7 +1960,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
title: Obx(() {
return Text(
// '${arguments['title']}',
'${arguments.value.showName}',
arguments.value.showName ?? '神秘人',
style: const TextStyle(fontSize: 18.0, fontFamily: 'Arial'),
);
}),
@ -1693,6 +1979,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
color: Colors.white,
),
onPressed: () async {
await getBlackList();
final paddingTop = MediaQuery.of(Get.context!).padding.top;
final selected = await showMenu(
@ -1754,7 +2041,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
Icon(Icons.block, color: Colors.white, size: 18),
SizedBox(width: 8),
Text(
'拉黑',
blackTxt,
style: TextStyle(color: Colors.white),
),
],
@ -1788,10 +2075,34 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
// break;
case 'report':
print('点击了举报');
// print('点击了举报');
Get.to(() => ReportChat(convsationID: arguments.value.conversationID));
break;
case 'block':
print('点击了拉黑');
// print('点击了拉黑');
String showTxt = blackTxt == '拉黑' ? '拉黑后将不保留任何聊天记录' : '';
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text('确认要$blackTxt对方吗$showTxt', 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('点击了取关');
@ -1925,7 +2236,15 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
),
onPanStart: (details) async {
//
logger.w('开始录音');
final hasPer = await Permission.microphone.status;
logger.w('开始录音:${hasPer.toString()}');
if (hasPer.isGranted) {
//
setState(() {
hasMicPermission = true;
});
final res = await VoiceService().startRecording();
if (res) {
setState(() {
@ -1935,8 +2254,21 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
} else {
MyDialog.toast('未获得麦克风权限');
}
} else {
//
hasMicPermission = false;
final granted = await Permissions.requestMicrophonePermission(
title: '麦克风权限使用说明',
content: '聊天时发送语音消息',
);
if (!granted) {
Permissions.showPermissionDialog('麦克风');
}
return;
}
},
onPanUpdate: (details) {
if (!hasMicPermission) return;
Offset pos = details.globalPosition;
double swipeY = MediaQuery.of(context).size.height - 120;
double swipeX = MediaQuery.of(context).size.width / 2 + 50;
@ -1952,6 +2284,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
},
onPanEnd: (details) {
logger.w('停止录音');
if (!hasMicPermission) return;
setState(() {
switch (voiceType) {
case 1:

View File

@ -8,22 +8,30 @@ 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/permissions.dart';
import 'package:loopin/utils/snapshot.dart';
import 'package:loopin/utils/voice_service.dart';
import 'package:mime/mime.dart';
import 'package:permission_handler/permission_handler.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';
@ -42,6 +50,7 @@ class ChatGroup extends StatefulWidget {
class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMixin {
final ImagePicker _picker = ImagePicker();
bool hasMicPermission = false; //
late final ChatDetailController controller;
//
@ -79,7 +88,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'},
];
@ -377,6 +386,7 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
borderRadius: BorderRadius.circular(10.0),
child: NetworkOrAssetImage(
imageUrl: item.videoElem?.snapshotUrl ?? '',
placeholderAsset: 'assets/images/bk.jpg',
width: 120,
),
),
@ -535,6 +545,7 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
children: [
NetworkOrAssetImage(
imageUrl: url,
placeholderAsset: 'assets/images/bk.jpg',
width: 160.0,
),
Container(
@ -662,10 +673,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 +705,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 +1168,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;
@ -1281,6 +1331,11 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
///
Future<void> _pickPhoto() async {
final hasPer = await Permissions.requestCameraPermission(title: '相机权限使用说明', content: '聊天时发送拍摄的照片');
if (!hasPer) {
Permissions.showPermissionDialog('相机');
return;
}
final XFile? photo = await _picker.pickImage(
source: ImageSource.camera,
maxWidth: 1920,
@ -1295,6 +1350,11 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
///
Future<void> _pickVideo() async {
final hasPer = await Permissions.requestCameraPermission(title: '相机权限使用说明', content: '聊天时发送拍摄的视频');
if (!hasPer) {
Permissions.showPermissionDialog('相机');
return;
}
final XFile? video = await _picker.pickVideo(
source: ImageSource.camera,
maxDuration: const Duration(minutes: 1),
@ -1419,9 +1479,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 +1522,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,7 +1533,10 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
SizedBox(
height: 100.0,
),
if (open == false && data.isSelf == false)
if (open == false) ...[
//
if (hasGet == false)
//
AnimatedBuilder(
animation: animTurns,
builder: (context, child) {
@ -1490,20 +1562,63 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
//
//--------
//
obj['open'] = true; //true
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);
//
await 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);
await 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();
}
},
),
);
},
),
]
],
),
),
@ -1565,7 +1680,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))),
@ -1579,11 +1701,27 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
builder: (context) {
return RedPacket(
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 {
@ -1603,6 +1741,7 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
@override
Widget build(BuildContext context) {
//editorFocusNode
return Stack(
fit: StackFit.expand,
children: [
@ -1625,7 +1764,7 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
),
titleSpacing: 1.0,
title: Text(
'${arguments.showName}',
arguments.showName ?? '群聊',
style: const TextStyle(fontSize: 18.0, fontFamily: 'Arial'),
maxLines: 1,
overflow: TextOverflow.ellipsis,
@ -1797,6 +1936,14 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
),
onPanStart: (details) async {
//
final hasPer = await Permission.microphone.status;
logger.w('开始录音:${hasPer.toString()}');
if (hasPer.isGranted) {
//
setState(() {
hasMicPermission = true;
});
final res = await VoiceService().startRecording();
if (res) {
setState(() {
@ -1806,8 +1953,22 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
} else {
MyDialog.toast('未获得麦克风权限');
}
} else {
//
hasMicPermission = false;
final granted = await Permissions.requestMicrophonePermission(
title: '麦克风权限使用说明',
content: '聊天时发送语音消息',
);
if (!granted) {
Permissions.showPermissionDialog('麦克风');
}
return;
}
},
onPanUpdate: (details) {
if (!hasMicPermission) return;
Offset pos = details.globalPosition;
double swipeY = MediaQuery.of(context).size.height - 120;
double swipeX = MediaQuery.of(context).size.width / 2 + 50;
@ -1823,6 +1984,8 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
},
onPanEnd: (details) {
// print('停止录音');
if (!hasMicPermission) return;
setState(() {
switch (voiceType) {
case 1:

View File

@ -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'},
];
@ -441,6 +441,7 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
child: NetworkOrAssetImage(
imageUrl: item.videoElem?.snapshotUrl ?? '',
width: 120,
placeholderAsset: 'assets/images/bk.jpg',
),
),
const Align(
@ -605,6 +606,7 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
children: [
NetworkOrAssetImage(
imageUrl: url,
placeholderAsset: 'assets/images/bk.jpg',
width: 160.0,
),
Container(
@ -1515,7 +1517,7 @@ class _ChatNoFriendState extends State<ChatNoFriend> with SingleTickerProviderSt
title: Obx(() {
return Text(
// '${arguments['title']}',
'${arguments.value.showName}',
arguments.value.showName ?? '陌生人',
style: const TextStyle(fontSize: 18.0, fontFamily: 'Arial'),
);
}),

View File

@ -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,
},

View 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);
}
}

View File

@ -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;
@ -66,12 +68,19 @@ class ChatPageState extends State<ChatPage> {
//
void handleScanResult(String code) {
print('扫码结果11111111111111111111111$code');
ScanCodeType.promotion;
// 使
final scanType = ScanCodeType.fromCode(code);
if (scanType != null) {
logger.e(scanType);
if (code.contains('HXM')) {
_processScanCode(scanType, code);
} else {
final value = code.substring(scanType.prefix.length + 1); //
_processScanCode(scanType, value);
}
} else {
//
Get.snackbar('未知的二维码类型', '无法识别此二维码: $code');
@ -95,9 +104,18 @@ class ChatPageState extends State<ChatPage> {
//
void _handleVerificationCode(String value) async {
print('处理核销码: $value');
logger.e('处理核销码: $value');
final controller = Get.find<ImUserInfoController>();
final role = controller.role.value;
final isSeller = Utils.hasRole(role, 2);
// writeOffCodeId
if (isSeller) {
//
Get.toNamed('/sellerOrder/detail', arguments: {'writeOffCodeId': value});
} else {
//
MyToast().tip(title: '未入驻的商家不可操作');
}
}
//
@ -315,19 +333,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 +363,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 +582,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,
@ -624,6 +644,8 @@ class ChatPageState extends State<ChatPage> {
),
),
onTap: () {
// logger.e(chatList[index].isCustomAdmin);
if (conversationTypeFromString(chatList[index].isCustomAdmin) != null) {
// notify下的内容
logger.e(chatList[index].isCustomAdmin);
@ -660,4 +682,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;
}
}
}

View File

@ -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 {
@ -55,8 +56,18 @@ class _AddFriendState extends State<AddFriend> {
//
void _handleVerificationCode(String value) async {
logger.w('处理核销码: $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});
}
//

View File

@ -44,7 +44,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 +58,7 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
final lastmsg = conv?.lastMessage;
if (lastmsg != null) {
msgList.add(lastmsg);
getMsgData();
}
}
// chatController.addListener(() {
@ -74,11 +75,23 @@ 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 {
//
V2TimMessage? lastRealMsg;
lastRealMsg = msgList.last;
lastRealMsg = msgList.isEmpty ? null : msgList.last;
final res = await ImService.instance.getHistoryMessageList(
userID: ConversationType.interaction.name, // userID为固定的interaction
lastMsg: lastRealMsg,
@ -103,13 +116,34 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
//
void _filterMessages(String filterType) async {
logger.e(filterType);
if (filterType == 'all') {
getMsgData();
return;
}
//
String keyword;
// interactionComment, //->
// interactionAt, //->@
// interactionLike, //->
// interactionReply, //->
if (filterType == 'interactionComment') {
keyword = '评论';
} else if (filterType == 'interactionLike') {
keyword = '点赞';
} else if (filterType == 'interactionReply') {
keyword = '回复';
} else {
keyword = '视频'; //
}
final res = await ImService.instance.searchLocalMessages(
page: page,
conversationID: 'c2c_${ConversationType.interaction.name}',
keywordList: ['action', filterType],
keywordList: [keyword],
// keywordList: ['action', filterType], //interactionLike
);
logger.e(res.data!.toLogString());
logger.e(res.data!.toJson());
if (res.success && res.data != null) {
msgList.clear();
final resultList = res.data?.messageSearchResultItems ?? [];
if (resultList.isNotEmpty) {
for (var item in resultList) {
@ -117,6 +151,7 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
msgList.addAll(item.messageList ?? []);
}
} else {
msgList.clear();
logger.e('数据为空${res.desc}');
}
}
@ -229,15 +264,15 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
child: EasyRefresh.builder(
callLoadOverOffset: 20, //
callRefreshOverOffset: 20, //
header: ClassicHeader(
dragText: '下拉刷新',
armedText: '释放刷新',
readyText: '加载中...',
processingText: '加载中...',
processedText: '加载完成',
failedText: '加载失败,请重试',
messageText: '最后更新于 %T',
),
// header: ClassicHeader(
// dragText: '下拉刷新',
// armedText: '释放刷新',
// readyText: '加载中...',
// processingText: '加载中...',
// processedText: '加载完成',
// failedText: '加载失败,请重试',
// messageText: '最后更新于 %T',
// ),
footer: ClassicFooter(
dragText: '加载更多',
armedText: '释放加载',
@ -248,11 +283,11 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
failedText: '加载失败,请重试',
messageText: '最后更新于 %T',
),
onRefresh: () async {
await handleRefresh();
},
// onRefresh: () async {
// await handleRefresh();
// },
onLoad: () async {
if (hasMore.value) {
if (selectedMessageType.value != 'all' && hasMore.value) {
await getMsgData();
return hasMore.value ? IndicatorResult.success : IndicatorResult.noMore;
}
@ -260,14 +295,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 +311,20 @@ 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 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); //
logger.e(item);
// ----
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);
// 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),
@ -294,16 +332,14 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
spacing: 10.0,
children: <Widget>[
//
InkWell(
GestureDetector(
onTap: () async {
//
//
// cloudCustomData是interactionCommentinteractionAtinteractionReply,id
//
// final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
//
// Get.toNamed('/vloger', arguments: res['data']);
Get.toNamed('/vloger');
Get.toNamed('/vloger', arguments: {'memberId': item['userID']});
},
child: ClipOval(
child: NetworkOrAssetImage(
@ -316,15 +352,13 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
//
Expanded(
child: InkWell(
onTap: () {
child: GestureDetector(
onTap: () async {
//
//
// cloudCustomData是interactionCommentinteractionAtinteractionReply,id
//
// final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
// Get.toNamed('/vloger', arguments: res['data']);
Get.toNamed('/vloger');
logger.e(item);
Get.toNamed('/videoDetail', arguments: {'videoId': item['vlogID']});
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -343,8 +377,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),
@ -359,7 +392,11 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
),
//
Row(
GestureDetector(
onTap: () {
Get.toNamed('/videoDetail', arguments: {'videoId': item['vlogID']});
},
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Visibility(
@ -380,6 +417,7 @@ class InteractionState extends State<Interaction> with SingleTickerProviderState
),
],
),
),
],
),
);

View File

@ -7,12 +7,10 @@ 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';
@ -48,10 +46,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 +183,8 @@ 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.e(item);
logger.w(element.toJson());
@ -196,9 +208,7 @@ class NewfoucsState extends State<Newfoucs> with SingleTickerProviderStateMixin
//
// cloudCustomData是interactionCommentinteractionAtinteractionReply,id
//
final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
Get.toNamed('/vloger', arguments: res['data']);
// Get.toNamed('/vloger');
Get.toNamed('/vloger', arguments: {'memberId': item['userID']});
},
child: ClipOval(
child: NetworkOrAssetImage(
@ -214,8 +224,8 @@ class NewfoucsState extends State<Newfoucs> with SingleTickerProviderStateMixin
child: InkWell(
onTap: () async {
// ,
final res = await Http.get('${VideoApi.detail}/${item['vlogID']}');
Get.toNamed('/vloger', arguments: res['data']);
Get.toNamed('/vloger', arguments: {'memberId': item['userID']});
// Get.toNamed('/vloger');
},
child: Column(

View File

@ -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是interactionCommentinteractionAtinteractionReply,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),
),
],
),
],
),
);
},
);
},
);
},
),
),
],
),
),
);
}
}

View 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';
}
}
}

View File

@ -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]);
},
),
);
},
);
})),
),
],
),
),
);
}
}

View File

@ -108,8 +108,9 @@ class _GoodsState extends State<Goods> {
Get.back();
}
}
// SKU
void locateSelectedSku() {
void locateSelectedSku() {
if (shopObj != null && shopObj['skuList'] != null) {
for (var sku in shopObj['skuList']) {
try {
@ -136,29 +137,34 @@ void locateSelectedSku() {
}
}
}
}
}
//
void handleAttributeSelect(String attrName, String optionName) {
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 {
@ -184,7 +190,8 @@ void handleAttributeSelect(String attrName, String optionName) {
void handlCoverClick(V2TimConversation conv) async {
//
final userId = conv.userID;
final isGroup = conv.groupID != null && (conv.groupID ?? '').isNotEmpty;
final id = isGroup ? conv.groupID : conv.userID;
//price,title,url,sell
logger.w(shopObj['name']);
final makeJson = jsonEncode({
@ -199,7 +206,12 @@ void handleAttributeSelect(String attrName, String optionName) {
data: makeJson,
);
if (res.success) {
final sendRes = await IMMessage().sendMessage(msg: res.data!.messageInfo!, toUserID: userId, cloudCustomData: SummaryType.shareTuangou);
final sendRes = await IMMessage().sendMessage(
msg: res.data!.messageInfo!,
groupID: isGroup ? id : null,
toUserID: isGroup ? null : id,
cloudCustomData: SummaryType.shareTuangou,
);
if (sendRes.success) {
MyToast().tip(
title: '分享成功',
@ -338,6 +350,7 @@ void handleAttributeSelect(String attrName, String optionName) {
},
);
}
//
bool isAttributeSelected(String attrName, String optionName) {
return selectedAttributes[attrName] == optionName;
@ -545,7 +558,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 +700,7 @@ void handleAttributeSelect(String attrName, String optionName) {
SizedBox(height: 12),
],
);
}).toList(),
}),
],
),
),
@ -736,7 +749,8 @@ void handleAttributeSelect(String attrName, String optionName) {
color: Color(0xFFFFEBEB),
borderRadius: BorderRadius.circular(30.0),
),
child: shopObj['canOrder'] == true?Row(
child: shopObj['canOrder'] == true
? Row(
children: [
Container(
alignment: Alignment.center,
@ -746,6 +760,7 @@ void handleAttributeSelect(String attrName, String optionName) {
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});
@ -760,7 +775,8 @@ void handleAttributeSelect(String attrName, String optionName) {
),
),
],
):null,
)
: null,
),
],
),

View File

@ -1,6 +1,7 @@
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:loopin/IM/im_service.dart';
import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/styles/index.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_full_info.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
@ -184,6 +185,7 @@ class _MemberActionSheetState extends State<InviteActionSheet> {
final showName = uname.isEmpty ? nickname : uname;
return InkWell(
onTap: () {
FocusScope.of(context).unfocus();
setState(() {
if (_selectedIDs.contains(id)) {
_selectedIDs.remove(id);
@ -196,12 +198,15 @@ class _MemberActionSheetState extends State<InviteActionSheet> {
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
child: Row(
children: [
//
CircleAvatar(
radius: 20,
backgroundImage: m.faceUrl != null ? NetworkImage(m.faceUrl!) : null,
child: m.faceUrl == null ? const Icon(Icons.person) : null,
//
ClipOval(
child: NetworkOrAssetImage(
imageUrl: m.faceUrl ?? '',
width: 40.0,
height: 40.0,
),
),
const SizedBox(width: 12),
//

View File

@ -2,6 +2,7 @@ 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/components/network_or_asset_image.dart';
import 'package:loopin/pages/groupChat/controller/group_detail_controller.dart';
import 'package:loopin/styles/index.dart';
import 'package:tencent_cloud_chat_sdk/enum/group_member_filter_enum.dart';
@ -50,6 +51,7 @@ class _MemberActionSheetState extends State<MemberActionSheet> {
final self = Get.find<GroupDetailController>().selfInfo.value!;
members.insert(0, self);
}
getMemberData();
}
@ -59,7 +61,7 @@ class _MemberActionSheetState extends State<MemberActionSheet> {
loading = true;
final res = await ImService.instance.getGroupMemberList(
groupID: widget.groupID,
filter: GroupMemberFilterTypeEnum.V2TIM_GROUP_MEMBER_FILTER_COMMON,
filter: GroupMemberFilterTypeEnum.V2TIM_GROUP_MEMBER_FILTER_ALL,
nextSeq: nextSeq,
count: 100,
);
@ -67,8 +69,13 @@ class _MemberActionSheetState extends State<MemberActionSheet> {
logger.e(res.data!.nextSeq);
nextSeq = res.data!.nextSeq ?? '0';
final mem = res.data!.memberInfoList ?? [];
setState(() {
//
if (widget.showSelf) {
members.addAll(mem);
members = {for (var m in members) m.userID: m}.values.toList();
}
setState(() {
hasMore = res.data!.nextSeq == '0' ? false : true;
loading = false;
});
@ -131,7 +138,7 @@ class _MemberActionSheetState extends State<MemberActionSheet> {
@override
Widget build(BuildContext context) {
List filteredMembers;
List<V2TimGroupMemberFullInfo> filteredMembers;
if (_query.isEmpty) {
filteredMembers = members;
} else {
@ -223,23 +230,36 @@ class _MemberActionSheetState extends State<MemberActionSheet> {
final showName = uname.isEmpty ? nickname : uname;
return InkWell(
onTap: () {
FocusScope.of(context).unfocus();
setState(() {
if (widget.showSelf) {
// =,
final selfInfo = Get.find<GroupDetailController>().selfInfo.value ?? V2TimGroupMemberFullInfo(userID: '');
final currentUserID = selfInfo.userID;
if (m.userID != currentUserID) {
Get.toNamed('/vloger', arguments: {'memberId': m.userID});
}
} else {
// false=
if (_selectedIDs.contains(id)) {
_selectedIDs.remove(id);
} else {
_selectedIDs.add(id);
}
}
});
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
child: Row(
children: [
//
CircleAvatar(
radius: 20,
backgroundImage: m.faceUrl != null ? NetworkImage(m.faceUrl!) : null,
child: m.faceUrl == null ? const Icon(Icons.person) : null,
//
ClipOval(
child: NetworkOrAssetImage(
imageUrl: m.faceUrl ?? '',
width: 40.0,
height: 40.0,
),
),
const SizedBox(width: 12),

View File

@ -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';
@ -165,16 +166,34 @@ class GroupdetailState extends State<Groupdetail> {
);
}
},
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Flexible(
child: Text(
Utils.handleText(controller.info.value?.groupName, '', "未命名群聊"),
// '很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长',
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
SizedBox(
width: 10,
),
if (controller.isOwner.value)
Icon(
Icons.edit,
size: 14,
)
],
),
),
),
//
subtitle: Obx(
subtitle: Padding(
padding: const EdgeInsets.only(top: 6.0),
child: Obx(
() => GestureDetector(
onTap: () {
// setinfo页
@ -199,17 +218,64 @@ class GroupdetailState extends State<Groupdetail> {
},
),
);
} else {
//
showModalBottomSheet(
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
isScrollControlled: true, //
builder: (ctx) => SafeArea(
child: Container(
width: double.infinity, // 👈
padding: const EdgeInsets.all(16),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min, //
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"群简介",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
Text(
Utils.handleText(controller.info.value?.introduction, '', "暂无群介绍"),
style: const TextStyle(fontSize: 14),
),
const SizedBox(height: 16),
],
),
),
),
),
);
}
},
child: Row(mainAxisSize: MainAxisSize.max, children: [
Flexible(
child: Text(
Utils.handleText(controller.info.value?.introduction, '', "暂无群介绍"),
// '非常长的内容非常长的内容非常长的内容非常长的内容非常长的内容非常长的内容',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: Colors.grey),
),
),
SizedBox(
width: 10,
),
trailing: const Icon(Icons.chevron_right),
if (controller.isOwner.value)
Icon(
Icons.edit,
size: 14,
)
]),
),
),
),
// trailing: const Icon(Icons.chevron_right),
),
const Divider(height: 1),
@ -251,19 +317,12 @@ class GroupdetailState extends State<Groupdetail> {
);
},
),
//
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Wrap(
spacing: 16,
runSpacing: 12,
children: [
Obx(
() {
return Wrap(
spacing: 16,
runSpacing: 12,
children: controller.memberList.take(8).map((m) {
//
child: Obx(() {
//
final memberWidgets = controller.memberList.take(8).map((m) {
return GestureDetector(
onTap: () {
final currentUserID = controller.selfInfo.value?.userID ?? '';
@ -287,25 +346,22 @@ class GroupdetailState extends State<Groupdetail> {
child: Text(
m.nickName ?? '未知昵称',
maxLines: 1,
softWrap: false,
overflow: TextOverflow.ellipsis,
softWrap: false,
style: const TextStyle(fontSize: 12),
textAlign: TextAlign.center,
),
),
],
),
);
}).toList(),
);
},
),
// +
GestureDetector(
}).toList();
//
final inviteWidget = GestureDetector(
onTap: () {
//,
logger.w('当前人数${controller.info.value?.memberCount ?? 0}---上限人数${controller.info.value?.memberMaxCount ?? 0}');
if ((controller.info.value?.memberCount ?? 0) < (controller.info.value?.memberMaxCount ?? 0)) {
//
showModalBottomSheet(
context: context,
barrierColor: Colors.white,
@ -330,19 +386,17 @@ class GroupdetailState extends State<Groupdetail> {
}
},
child: Column(
children: [
CircleAvatar(
radius: 25,
child: const Icon(Icons.add),
),
const SizedBox(height: 4),
const Text("邀请", style: TextStyle(fontSize: 12)),
mainAxisSize: MainAxisSize.min,
children: const [
CircleAvatar(radius: 25, child: Icon(Icons.add)),
SizedBox(height: 4),
Text("邀请", style: TextStyle(fontSize: 12)),
],
),
),
// -
Obx(
() => controller.isOwner.value
);
//
final removeWidget = controller.isOwner.value
? GestureDetector(
onTap: () {
showModalBottomSheet(
@ -366,20 +420,32 @@ class GroupdetailState extends State<Groupdetail> {
);
},
child: Column(
children: [
const CircleAvatar(
radius: 25,
child: Icon(Icons.remove),
),
const SizedBox(height: 4),
const Text("移除", style: TextStyle(fontSize: 12)),
mainAxisSize: MainAxisSize.min,
children: const [
CircleAvatar(radius: 25, child: Icon(Icons.remove)),
SizedBox(height: 4),
Text("移除", style: TextStyle(fontSize: 12)),
],
),
)
: SizedBox.shrink(),
)
],
),
: const SizedBox.shrink();
final allWidgets = <Widget>[
...memberWidgets,
inviteWidget,
];
if (controller.isOwner.value) allWidgets.add(removeWidget);
return GridView.count(
shrinkWrap: true, //
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: 4, // 4
mainAxisSpacing: 0, //
crossAxisSpacing: 16, //
childAspectRatio: 1, //
children: allWidgets,
);
}),
),
const Divider(height: 1),
@ -500,13 +566,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),

View 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);
}
}

View File

@ -1,19 +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 'package:loopin/utils/network_utils.dart';
import '../../behavior/custom_scroll_behavior.dart';
import '../../components/backtop.dart';
class IndexPage extends StatefulWidget {
const IndexPage({super.key});
@ -22,30 +29,237 @@ 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': '美的MideaLED便携充电小台灯书桌学习阅读灯学生宿舍卧室床头灯学习台灯',
'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;
final NetworkUtils networkUtils = NetworkUtils();
/// 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 {
dataList.clear();
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);
if (dataList.length >= total) {
hasMore.value = false;
isLoading.value = false;
return;
}
dataList.value = data;
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);
if (dataList.length >= total) {
hasMore.value = false;
isLoading.value = false;
return;
}
dataList.addAll(data);
page += 1;
logger.e(page);
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);
}
}
});
//
ever(
networkUtils.isConnected,
(status) {
logger.e('商品首页当前网络状态:$status');
if (status) {
initTabs();
}
},
);
//
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 +273,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,219 +326,256 @@ 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(
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: [
_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),
TextButton(
onPressed: () {
focusNode.unfocus();
if (textEditingController.text.isNotEmpty) {
// tab索引
Get.toNamed(
'/search-result',
arguments: {'searchWords': textEditingController.text, 'tab': 1},
);
}).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,
);
},
),
child: Text('搜索'),
),
],
),
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,
);
}),
),
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();
//
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(
child: Swiper.children(
pagination: SwiperPagination(
builder: DotSwiperPaginationBuilder(
color: Colors.white70,
activeColor: Colors.white,
size: 6.0,
activeSize: 8.0,
space: 4.0,
),
),
itemBuilder: (context, index) {
final imageUrl = controller.swiperData[index]['images'] ?? '';
return imageUrl.isNotEmpty ? Image.network(imageUrl, fit: BoxFit.fill) : const SizedBox.shrink();
},
indicatorLayout: PageIndicatorLayout.SCALE,
children: swiperData.map<Widget>((itm) {
return NetworkOrAssetImage(
imageUrl: itm['images'],
placeholderAsset: 'assets/images/bk.jpg',
);
}).toList(),
),
);
}),
],
);
}
//
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);
//
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);
}
},
header: ClassicHeader(
dragText: '下拉刷新',
armedText: '释放刷新',
readyText: '加载中...',
processingText: '加载中...',
processedText: '加载完成',
messageText: '最后更新于 %T',
child: Column(
spacing: 3.0,
children: [
ClipOval(
child: NetworkOrAssetImage(
imageUrl: citem['icon'],
width: 30.0,
height: 30.0,
placeholderAsset: 'assets/images/wait_loading.png',
),
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(
Text(citem['name']),
],
),
);
},
);
},
),
),
//
CustomPageViewIndicator(
controller: pageController,
count: (tabList.length / 4).ceil(),
color: Color(0xFFCECECE),
activeColor: Color(0xFFFF5000),
),
],
),
);
},
),
),
),
),
//
SliverToBoxAdapter(
child: Obx(
() {
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,
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()),
),
),
itemCount: dataList.length,
itemBuilder: (BuildContext context, int index) {
return cardList(dataList[index]);
},
),
Opacity(opacity: dataList.isNotEmpty && isLoading.value ? 1 : 0, child: Loading(title: 'loading...')),
],
),
);
});
}
//
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
View 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),
),
],
),
),
);
}
}

View File

@ -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 ? '团长' : '',
)
],
),

View File

@ -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 == '/';
},
);
}
}
}

View File

@ -21,13 +21,14 @@ import 'package:loopin/utils/scan_code_type.dart';
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_follow_info.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../utils/common.dart';
class PageParams {
int page;
int pageSize;
bool isLoading;
RxBool isLoading;
RxBool hasMore;
int total;
bool isInitLoading;
@ -35,16 +36,21 @@ class PageParams {
PageParams({
this.page = 1,
this.pageSize = 10,
this.isLoading = false,
bool isLoading = false,
bool hasMore = true,
this.total = 0,
this.isInitLoading = true,
}) : hasMore = hasMore.obs;
}) : hasMore = hasMore.obs,
isLoading = isLoading.obs;
@override
String toString() {
return 'PageParams(page: $page, pageSize: $pageSize, isLoading: ${isLoading.value}, hasMore: ${hasMore.value})';
}
void init() {
page = 1;
pageSize = 10;
isLoading = false;
isLoading.value = false;
hasMore.value = true;
total = 0;
isInitLoading = true;
@ -66,7 +72,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,24 +94,25 @@ 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 = () {
final pos = scrollController.position;
final isNearBottom = pos.pixels >= pos.maxScrollExtent - 100;
if (!isNearBottom) return;
if (currentTabIndex.value == 0 && !itemsParams.isLoading && itemsParams.hasMore.value) {
if (currentTabIndex.value == 0 && !itemsParams.isLoading.value && itemsParams.hasMore.value) {
loadData(0);
} else if (currentTabIndex.value == 1 && !favoriteParams.isLoading && favoriteParams.hasMore.value) {
} else if (currentTabIndex.value == 1 && !favoriteParams.isLoading.value && favoriteParams.hasMore.value) {
loadData(1);
}
};
@ -121,8 +129,6 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
}
};
tabController.addListener(tabListener);
// loadData(0);
}
@override
@ -140,7 +146,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) {}
}
@ -162,13 +168,13 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
Future<void> loadData([int? tabIndex]) async {
final index = tabIndex ?? currentTabIndex.value;
if (index == 0) {
if (itemsParams.isLoading || !itemsParams.hasMore.value) return;
itemsParams.isLoading = true;
// itemsParams.isInitLoading = true;
if (itemsParams.isLoading.value || !itemsParams.hasMore.value) return;
WidgetsBinding.instance.addPostFrameCallback((_) {
itemsParams.isLoading.value = 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,
@ -176,28 +182,25 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
final obj = res['data'];
final total = obj['total'];
final row = obj['records'] ?? [];
logger.i(res['data']);
//
logger.e(items.length);
//
items.addAll(row);
logger.e(obj);
itemsParams.page++;
if (items.length >= total) {
itemsParams.hasMore.value = false;
}
//
itemsParams.page++;
//
itemsParams.isLoading = false;
itemsParams.isLoading.value = false;
itemsParams.isInitLoading = false;
} else if (index == 1) {
if (favoriteParams.isLoading || !favoriteParams.hasMore.value) return;
favoriteParams.isLoading = true;
// favoriteParams.isInitLoading = true;
if (favoriteParams.isLoading.value || !favoriteParams.hasMore.value) return;
WidgetsBinding.instance.addPostFrameCallback((_) {
favoriteParams.isLoading.value = 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,
@ -206,14 +209,14 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
final total = obj['total'];
final row = obj['records'] ?? [];
favoriteItems.addAll(row);
favoriteParams.page++;
if (favoriteItems.length >= total) {
favoriteParams.hasMore.value = false;
}
favoriteParams.page++;
favoriteParams.isLoading = false;
favoriteParams.isLoading.value = false;
favoriteParams.isInitLoading = false;
logger.e(favoriteParams.toString());
}
}
@ -255,7 +258,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 +272,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,
@ -384,9 +387,49 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
//
},
actions: [
// _buildIcon('assets/images/svg/service.svg', () {
// logger.i('点击客服按钮');
// }),
_buildIcon('assets/images/svg/service.svg', () {
logger.i('点击客服按钮');
showModalBottomSheet(
context: Get.context!,
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
builder: (context) {
return SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.mobile_friendly, color: Colors.black),
title: const Text('联系客服', style: TextStyle(color: Colors.black)),
onTap: () async {
Get.back();
final phoneNumber = '18832510385'; //
final url = Uri.parse('tel:$phoneNumber');
try {
await launchUrl(
url,
mode: LaunchMode.platformDefault,
);
} catch (e) {
Get.snackbar(
'错误',
'无法拨打电话',
duration: Duration(seconds: 3),
backgroundColor: Colors.red.withAlpha(230),
colorText: Colors.white,
icon: const Icon(Icons.error_outline, color: Colors.white),
);
}
},
),
],
),
);
},
);
}),
const SizedBox(width: 8.0),
_buildIcon('assets/images/svg/setting.svg', () {
logger.i('点击设置按钮');
@ -402,9 +445,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 +542,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,11 +563,13 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'${imUserInfoController!.signature}',
child: Obx(
() => Text(
'${imUserInfoController.signature}',
style: const TextStyle(fontSize: 16),
),
),
),
],
));
}
@ -538,11 +583,19 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
if (listToShow.isEmpty) {
return emptyTip('暂无相关数据');
}
return NotificationListener<ScrollNotification>(
onNotification: (notification) {
if (notification is ScrollUpdateNotification) {
final metrics = notification.metrics;
final isNearBottom = metrics.pixels == metrics.maxScrollExtent;
return Obx(() {
return CustomScrollView(
// physics: !isPinned.value ? NeverScrollableScrollPhysics() : AlwaysScrollableScrollPhysics(),
// physics: AlwaysScrollableScrollPhysics(),
if (isNearBottom && !params.isLoading.value && params.hasMore.value) {
loadData(tabIndex);
}
}
return false;
},
child: CustomScrollView(
key: PageStorageKey('myindex_$tabIndex'),
slivers: [
SliverPadding(
@ -571,15 +624,25 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
padding: EdgeInsets.symmetric(vertical: 20.0),
child: Center(
child: params.hasMore.value ? CircularProgressIndicator() : Text('没有更多数据了'),
child: Obx(
() {
if (params.isLoading.value) {
return SafeArea(top: false, child: CircularProgressIndicator());
} else if (!params.hasMore.value) {
return SafeArea(top: false, child: Text('没有更多数据了'));
} else {
return SizedBox(height: 20);
}
},
),
),
),
),
],
),
);
});
}
Widget _buildVdCard(item, tabIndex) {
@ -688,6 +751,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 +764,17 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
fit: StackFit.expand,
children: [
// Obx
Obx(() {
final bgUrl = imUserInfoController?.customInfo['coverBg'] ?? '';
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 +787,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 +803,24 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
children: [
Row(
children: [
// Obx
Obx(
() {
final nickname = imUserInfoController?.nickname.value ?? '';
return Container(
//
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: Colors.black.withAlpha(76),
borderRadius: BorderRadius.circular(20),
),
child: Text(
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 +874,15 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
),
const SizedBox(height: 8),
// ID Obx
Obx(() {
final userId = imUserInfoController?.userID.value ?? '';
return Container(
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: Colors.black.withAlpha(76),
borderRadius: BorderRadius.circular(20),
),
child: InkWell(
child: Obx(() {
final userId = imUserInfoController.userID.value ?? '';
return InkWell(
onTap: () {
Clipboard.setData(ClipboardData(text: userId));
MyDialog.toast(
@ -827,9 +892,9 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
);
},
child: Text('ID:$userId', style: const TextStyle(fontSize: 12, color: Colors.white)),
),
);
}),
),
],
),
),
@ -856,9 +921,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 +935,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 +947,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,10 +959,12 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
},
child: Column(
children: [
Text(
Obx(
() => Text(
'${followInfo.value?.followersCount ?? 0}',
style: const TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 3.0),
const Text('粉丝'),
],
@ -908,7 +976,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(

View File

@ -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;
}

View File

@ -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('加载完成');
}

View File

@ -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'] ?? '',
);
}
}

View File

@ -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(

View File

@ -242,8 +242,8 @@ class _UserInfoState extends State<UserInfo> {
if (file != null) {
final fileSizeInBytes = await file.length();
final sizeInMB = fileSizeInBytes / (1024 * 1024);
if (sizeInMB > 200) {
MyDialog.toast('图片大小不能超过200MB', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
if (sizeInMB > 20) {
MyDialog.toast('图片大小不能超过20MB', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
} else {
print("图片合法,大小:$sizeInMB MB");
//upload(file)url地址
@ -291,7 +291,6 @@ class _UserInfoState extends State<UserInfo> {
} else {
print("图片合法,大小:$sizeInMB MB");
//upload(file)url地址
final croppedFile = await ImageCropper().cropImage(
sourcePath: file.path,
maxWidth: 1024,

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart';
import 'package:get/get.dart';
import 'package:get/get_rx/src/rx_typedefs/rx_typedefs.dart';
import 'package:loopin/IM/controller/chat_controller.dart';
@ -16,20 +17,26 @@ 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 {
int page;
int pageSize;
bool isLoading;
bool hasMore;
RxBool isLoading;
RxBool hasMore;
PageParams({
this.page = 1,
this.pageSize = 10,
this.isLoading = false,
this.hasMore = true,
});
this.pageSize = 12,
bool isLoading = false,
bool hasMore = true,
}) : hasMore = hasMore.obs,
isLoading = isLoading.obs;
@override
String toString() {
return 'PageParams(page: $page, pageSize: $pageSize, isLoading: ${isLoading.value}, hasMore: ${hasMore.value})';
}
}
class Vloger extends StatefulWidget {
@ -55,6 +62,7 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
},
role: 0,
));
String blackTxt = '拉黑';
late RxInt followed = 0.obs; //
// followersCount粉丝,mutualFollowersCount互关,followingCount我关注了多少人
@ -64,7 +72,7 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
));
RxBool get shouldFixHeader => (currentTabIndex.value == 0 && items.isEmpty) || (currentTabIndex.value == 1 && favoriteItems.isEmpty) ? true.obs : false.obs;
RxInt totalCount = 0.obs;
List tabList = [
{'name': "作品"},
];
@ -82,24 +90,21 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
void initState() {
super.initState();
args = Get.arguments ?? {};
print('argsssssssssssssssssssssss$args');
logger.e(args);
itemsParams = PageParams();
favoriteParams = PageParams();
selfInfo();
flowInfo();
checkFollowType();
initControllers();
//
scrollListener = () {
final pos = scrollController.position;
final isNearBottom = pos.pixels >= pos.maxScrollExtent - 100;
if (!isNearBottom) return;
if (currentTabIndex.value == 0 && !itemsParams.isLoading && itemsParams.hasMore) {
if (currentTabIndex.value == 0 && !itemsParams.isLoading.value && itemsParams.hasMore.value) {
loadData(0);
} else if (currentTabIndex.value == 1 && !favoriteParams.isLoading && favoriteParams.hasMore) {
loadData(1);
}
};
scrollController.addListener(scrollListener);
@ -113,6 +118,7 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
loadData(0);
getUserLikesCount();
getBlackList();
}
@override
@ -129,18 +135,27 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
void getUserLikesCount() async {
try {
final resData = await Http.get('${CommonApi.accountInfo}?memberId=${args['memberId']}');
print('aaaaaaaaaaaaaaaaaaa$resData');
logger.e('$resData');
if (resData != null && resData['code'] == 200) {
vlogLikeCount = resData['data']['vlogLikeCount'] ?? 0;
}
} catch (e) {}
} catch (e) {
logger.e(e);
}
}
void loadData([int? tabIndex]) async {
final index = tabIndex ?? currentTabIndex.value;
if (index == 0) {
if (itemsParams.isLoading || !itemsParams.hasMore) return;
itemsParams.isLoading = true;
if (itemsParams.isLoading.value || !itemsParams.hasMore.value) return;
WidgetsBinding.instance.addPostFrameCallback((_) {
itemsParams.isLoading.value = true;
});
// await Future.delayed(Duration(seconds: 5), () {
// logger.e('5秒后执行完毕');
// });
final res = await Http.post(VideoApi.getVideoListByMemberId, data: {
"memberId": args['memberId'],
"current": itemsParams.page,
@ -149,19 +164,20 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
final obj = res['data'];
final total = obj['total'];
final row = obj['records'] ?? [];
logger.i(res['data']);
// logger.i(res['data']);
totalCount.value = total;
//
logger.e(items.length);
//
items.addAll(row);
logger.e(obj);
if (items.length >= total) {
itemsParams.hasMore = false;
}
//
itemsParams.page++;
//
itemsParams.isLoading = false;
if (items.length >= total) {
itemsParams.hasMore.value = false;
}
itemsParams.isLoading.value = false;
logger.e(itemsParams.toString());
}
}
@ -175,7 +191,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 +226,75 @@ 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();
}
}
Widget _buildIcon(String assetPath, VoidCallback onTap) {
return InkWell(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
decoration: BoxDecoration(color: Colors.black.withAlpha((0.3 * 255).round()), borderRadius: BorderRadius.circular(20.0)),
child: SvgPicture.asset(assetPath, height: 20.0, width: 20.0, colorFilter: const ColorFilter.mode(Colors.white70, BlendMode.srcIn)),
),
);
}
@override
Widget build(BuildContext context) {
return PopScope(
@ -235,6 +320,72 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
collapsedHeight: 120.0,
pinned: true,
stretch: true,
actions: [
_buildIcon('assets/images/svg/more.svg', () 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;
}
}
}),
const SizedBox(width: 10.0),
],
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
@ -279,14 +430,20 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
controller: tabController,
tabs: tabList.map((item) {
return Tab(
child: Badge.count(
backgroundColor: Colors.red,
count: item['badge'] ?? 0,
isLabelVisible: item['badge'] != null,
alignment: Alignment.topRight,
offset: const Offset(14, -6),
child: Text(item['name'], style: const TextStyle(fontWeight: FontWeight.bold)),
child: Obx(
() => Text(
'${item['name']}(${totalCount.value})',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
// child: Badge.count(
// backgroundColor: Colors.red,
// count: item['badge'] ?? 0,
// isLabelVisible: item['badge'] != null,
// alignment: Alignment.topRight,
// offset: const Offset(14, -6),
// child: Text(item['name'], style: const TextStyle(fontWeight: FontWeight.bold)),
// ),
);
}).toList(),
isScrollable: true, //
@ -357,8 +514,19 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
if (listToShow.isEmpty) {
return emptyTip('暂无相关数据');
}
return NotificationListener<ScrollNotification>(
onNotification: (notification) {
if (notification is ScrollUpdateNotification) {
final metrics = notification.metrics;
final isNearBottom = metrics.pixels >= metrics.maxScrollExtent;
return CustomScrollView(
if (isNearBottom && !params.isLoading.value && params.hasMore.value) {
loadData(0);
}
}
return false;
},
child: CustomScrollView(
slivers: [
SliverPadding(
padding: const EdgeInsets.all(10.0),
@ -449,7 +617,7 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
crossAxisCount: 3,
crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0,
childAspectRatio: 0.7, //
childAspectRatio: 0.6, // 1=
),
),
),
@ -457,11 +625,22 @@ class MyPageState extends State<Vloger> with SingleTickerProviderStateMixin {
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: Center(
child: params.hasMore ? const CircularProgressIndicator() : const Text('没有更多数据了'),
child: Obx(
() {
if (params.isLoading.value) {
return SafeArea(top: false, child: CircularProgressIndicator());
} else if (!params.hasMore.value) {
return SafeArea(top: false, child: Text('没有更多数据了'));
} else {
return SizedBox(height: 20);
}
},
),
),
),
),
],
),
);
}

View File

@ -1,14 +1,20 @@
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/index.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,6 +26,8 @@ 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; //
@ -31,7 +39,8 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
@override
void initState() {
super.initState();
getOrderDetail(orderId: _orderId);
getOrderStatusDict();
// getOrderDetail(orderId: _orderId);
LifecycleHandler.onAppResumed = _onAppResumed;
}
@ -41,32 +50,39 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
super.dispose();
}
void _onAppResumed() {
print('App回到前台刷新订单状态,订单Id${_orderId}');
getOrderDetail(orderId: _orderId); //
void _onAppResumed() async {
print('App回到前台刷新订单状态,订单Id$_orderId');
await getOrderDetail(orderId: _orderId); //
//
logger.e(orderGoodsInfo['status']);
final int status = orderGoodsInfo['status'];
if (status == 0) {
//,
_showPaymentResultDialog(); //
}
}
//
void getOrderRealStatus({required String orderId}) async {
Future<void> getOrderRealStatus({required String orderId}) async {
try {
final res = await Http.get('${ShopApi.goodsOrderStatus}/$orderId');
Get.toNamed('/myOrder');
} catch (e) {
print('报错-------------->${e}');
print('报错-------------->$e');
}
}
//
void getOrderDetail({required String orderId}) async {
Future<void> getOrderDetail({required String orderId}) async {
try {
setState(() {
_isLoading = true;
});
final res = await Http.get('${ShopApi.goodsOrderDetail}/$orderId');
debugPrint(res['data'].toString(), wrapWidth: 600);
logger.w(res);
setState(() {
orderGoodsInfo = res['data'];
_initialSeconds = Utils.calcTime(orderGoodsInfo['createTime']);
_isLoading = false;
});
} catch (e) {
@ -77,6 +93,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 +111,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 +132,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
} else {
MyToast().tip(
title: '暂无可用的核销码',
position: 'center',
position: 'top',
type: 'error',
);
}
@ -229,8 +253,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 +311,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 +320,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 +371,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 +404,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 +471,7 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
],
);
case 1: //
case 2: //
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
@ -401,21 +487,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 +787,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 +808,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,7 +835,15 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
Row(
children: [
Spacer(),
Text(
orderGoodsInfo['items'][0]['status'] == 1
? Text(
'已核销',
style: TextStyle(
color: Colors.red,
fontWeight: FontWeight.bold,
),
)
: Text(
getOrderStatusText(orderGoodsInfo?['status']),
style: TextStyle(
color: getOrderStatusColor(orderGoodsInfo?['status']),
@ -842,7 +935,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 +949,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 +971,8 @@ class _OrderDetailState extends State<OrderDetail> with SingleTickerProviderStat
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
child: buildBottomButtons(),
),
),
)
: null,
);
}
}

View File

@ -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,37 @@ 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;
await Future.delayed(Duration.zero); // debug下偶尔出现问题
//
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 +149,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 +191,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 +234,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 +290,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 +333,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,7 +347,12 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
],
),
Spacer(),
Text(
order['items'][0]['status'] == 1
? Text(
'已核销',
style: TextStyle(color: Colors.red),
)
: Text(
_getStatusText(order['status']),
style: TextStyle(color: _getStatusColor(order['status'])),
)
@ -334,7 +361,8 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
SizedBox(height: 10),
//
if (order['items'] != null && order['items'].isNotEmpty)
...order['items'].map<Widget>((item) => Row(
...order['items']
.map<Widget>((item) => Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
@ -386,7 +414,8 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
),
)
],
)).toList(),
))
.toList(),
SizedBox(height: 10),
//
Container(
@ -422,8 +451,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 +554,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 +566,7 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
_refreshAllRelatedTabs();
}
} catch (e) {
print('取消订单失败-------------->${e}');
print('取消订单失败-------------->$e');
}
}
@ -546,9 +585,61 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
}
}
void _payOrder(_orderId) {
void _payOrder(orderId) 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);
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 +651,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;
}
});
}
@ -638,8 +745,7 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
child: Container(
color: Colors.grey[50],
child: Builder(
builder: (context) {
child: Builder(builder: (context) {
final currentOrders = _getOrderListByIndex(index);
final isLoading = _isLoading[index] ?? false;
final hasMoreData = _hasMoreData[index] ?? false;
@ -658,29 +764,36 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
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 +821,7 @@ class _MyOrderState extends State<MyOrder> with SingleTickerProviderStateMixin {
}
Widget emptyTip() {
return Container(
return SizedBox(
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,

View File

@ -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 {
@ -57,14 +60,33 @@ 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']}');
if (res == null) {
return;
}
setState(() {
orderGoodsInfo = res['data'];
_isLoading = false;
@ -78,45 +100,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?? '';
}
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=$writeOffCode');
debugPrint(res['data'].toString(), wrapWidth: 600);
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 +197,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 +270,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,7 +348,7 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
children: [],
);
case 1: //
case 2: //
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
@ -328,11 +366,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(
@ -393,7 +431,9 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
body: _isLoading
? Center(child: CircularProgressIndicator())
: orderGoodsInfo == null
? emptyTip()
? Center(
child: emptyTip(),
)
: ScrollConfiguration(
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
child: ListView(
@ -432,8 +472,6 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
style: TextStyle(color: Colors.orange),
),
SizedBox(width: 4),
],
),
SizedBox(height: 4),
@ -461,7 +499,16 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
Row(
children: [
Spacer(),
Text(
// orderGoodsInfo['items'][0]['status'] == 1
orderGoodsInfo['verificationCodeStatus'] == 1
? Text(
'已核销',
style: TextStyle(
color: Colors.red,
fontWeight: FontWeight.bold,
),
)
: Text(
getOrderStatusText(orderGoodsInfo?['status'] ?? 0),
style: TextStyle(
color: getOrderStatusColor(orderGoodsInfo?['status'] ?? 0),
@ -507,7 +554,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));
},
)
@ -516,11 +563,14 @@ class _SellerOrderDetailState extends State<SellerOrderDetail> with SingleTicker
SizedBox(height: 10),
Column(
children: [
_buildOrderInfoRow('订单号', orderGoodsInfo?['orderId']?.toString() ?? ''),
_buildOrderInfoRow('订单号', orderGoodsInfo?['orderSn']?.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 +588,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,15 +596,24 @@ 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;
}
Widget _buildOrderInfoRow(String label, String value) {
// logger.e(orderGoodsInfo);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6.0),
child: Row(

View File

@ -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,7 +357,12 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
],
),
Spacer(),
Text(
order['verificationCodeStatus'] == 1
? Text(
'已核销',
style: TextStyle(color: Colors.red),
)
: Text(
_getStatusText(order['status']),
style: TextStyle(color: _getStatusColor(order['status'])),
)
@ -333,23 +370,25 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
),
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.grey[200],
color: Colors.white,
borderRadius: BorderRadius.circular(4.0),
),
child: order['items'][0]['pic'] != null && order['items'][0]['pic'].isNotEmpty
? Image.network(
order['items'][0]['pic'],
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,
@ -363,7 +402,7 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item['productName'] ?? '商品名称',
order['productName'] ?? '商品名称',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 14),
@ -372,12 +411,12 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
Row(
children: [
Text(
'¥${item['salePrice'] ?? '0'}',
'¥${order['salePrice'] ?? '0'}',
style: TextStyle(color: Colors.red, fontSize: 16),
),
Spacer(),
Text(
'x${item['quantity'] ?? '1'}',
'x${order['quantity'] ?? '1'}',
style: TextStyle(color: Colors.grey),
),
],
@ -386,13 +425,13 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
),
)
],
)).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) {
void _payOrder(orderId) {
// pages/index/index
Wxsdk.openMiniApp(orderId: _orderId);
// 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;
}
});
}
@ -627,7 +694,9 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
),
),
),
body: TabBarView(
body: SafeArea(
bottom: true,
child: TabBarView(
controller: tabController,
children: List.generate(tabList.length, (index) {
return RefreshIndicator(
@ -638,8 +707,7 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
child: Container(
color: Colors.grey[50],
child: Builder(
builder: (context) {
child: Builder(builder: (context) {
final currentOrders = _getOrderListByIndex(index);
final isLoading = _isLoading[index] ?? false;
final hasMoreData = _hasMoreData[index] ?? false;
@ -658,28 +726,37 @@ class _SellerState extends State<SellerOrder> with SingleTickerProviderStateMixi
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;
}
}
@ -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,

View File

@ -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,7 +930,23 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
),
child: Row(
children: [
Container(
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(
@ -935,9 +961,27 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
),
child: user['avatar'] == null ? const Icon(Icons.person, color: Colors.grey, size: 20) : null,
),
),
const SizedBox(width: 10),
Expanded(
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(
@ -958,6 +1002,8 @@ class _SearchResultPageState extends State<SearchResultPage> with SingleTickerPr
],
),
),
),
//
ElevatedButton(
onPressed: () async {
await onFocusBtnClick(user, index);

View File

@ -56,8 +56,8 @@ class _UploadVideoPageState extends State<UploadVideoPage> {
Future<void> pickVideo() async {
descFocusNode.unfocus();
final hasPer = await Permissions.requestPhotoPermission();
final hasPer = await Permissions.requestVideoPermission();
logger.e(hasPer);
if (!hasPer) {
Permissions.showPermissionDialog('相册');
return;
@ -77,7 +77,7 @@ class _UploadVideoPageState extends State<UploadVideoPage> {
filterOptions: FilterOptionGroup(
videoOption: const FilterOption(
durationConstraint: DurationConstraint(
max: Duration(seconds: 30),
max: Duration(minutes: 30),
),
),
),
@ -90,8 +90,9 @@ class _UploadVideoPageState extends State<UploadVideoPage> {
if (file != null) {
final fileSizeInBytes = await file.length();
final sizeInMB = fileSizeInBytes / (1024 * 1024);
if (sizeInMB > 200) {
MyDialog.toast('文件大小不能超过200MB', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
//
if (sizeInMB > 300) {
MyDialog.toast('文件大小不能超过300MB', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
} else {
logger.i("文件合法,大小:$sizeInMB MB");
selectedVideo.value = pickedAssets.first;

View File

@ -285,14 +285,17 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
),
context: context,
builder: (context) {
return CommentBottomSheet(
return SafeArea(
child: CommentBottomSheet(
videoId: videoId,
onCommentCountChanged: (newCount) {
//
setState(() {
videoData['commentsCounts'] = newCount;
});
});
},
),
);
},
);
}
@ -455,13 +458,9 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
Future<void> _downloadVideoWithDio(String videoUrl, String fileName) async {
try {
//
var status = await Permissions.requestStoragePermission();
var status = await Permissions.requestStoragePermission(title: '文件存储使用说明', content: '用于下载视频');
if (!status) {
MyToast().tip(
title: '需要存储权限才能下载视频',
position: 'center',
type: 'error',
);
Permissions.showPermissionDialog('存储');
return;
}
await DownloadManager.downloadFile(
@ -603,8 +602,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,
@ -676,7 +682,7 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
),
child: ClipOval(
child: NetworkOrAssetImage(
imageUrl: videoData['vlogerFace'] ?? videoData['commentUserFace'],
imageUrl: videoData['vlogerFace'] ?? videoData['avatar'],
),
),
),
@ -801,7 +807,7 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'@${videoData['vlogerNickname'] ?? videoData['commentUserNickname'] ?? '未知用户'}',
'@${videoData['nickname'] ?? '未知用户'}',
style: const TextStyle(color: Colors.white, fontSize: 16.0),
),
LayoutBuilder(
@ -1172,8 +1178,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: [
@ -1204,7 +1210,7 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
GestureDetector(
onTap: () {
setState(() {
replyingCommentId = comment['id'];
replyingCommentId = comment['commentId'];
replyingCommentUser = comment['commentUserNickname'] ?? '未知用户';
});
},
@ -1232,10 +1238,10 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
GestureDetector(
onTap: () {
setState(() {
expandedReplies[comment['id']] = !isExpanded;
if (expandedReplies[comment['id']] == true &&
(replyData[comment['id']] == null || replyData[comment['id']]!.isEmpty)) {
fetchReplies(comment['id'], false);
expandedReplies[comment['commentId']] = !isExpanded;
if (expandedReplies[comment['commentId']] == true &&
(replyData[comment['commentId']] == null || replyData[comment['commentId']]!.isEmpty)) {
fetchReplies(comment['commentId'], false);
}
});
},
@ -1296,7 +1302,7 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
GestureDetector(
onTap: () {
setState(() {
replyingCommentId = comment['id'];
replyingCommentId = comment['commentId'];
replyingCommentUser = reply['commentUserNickname'] ?? '未知用户';
});
},
@ -1330,8 +1336,8 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
if (replies.length < comment['childCount'])
Center(
child: TextButton(
onPressed: () => fetchReplies(comment['id'], true),
child: isLoadingReplies[comment['id']] == true ? CircularProgressIndicator() : Text('加载更多回复'),
onPressed: () => fetchReplies(comment['commentId'], true),
child: isLoadingReplies[comment['commentId']] == true ? CircularProgressIndicator() : Text('加载更多回复'),
),
),
],

View File

@ -9,16 +9,10 @@ import '../../IM/im_core.dart';
import '../../behavior/custom_scroll_behavior.dart';
import '../../components/keepalive_wrapper.dart';
import '../../controller/video_module_controller.dart';
import './module/attention.dart';
// import './module/browse.dart';
// import './module/buying.dart';
// import './module/drama.dart';
// import './module/live.dart';
import './module/friend.dart';
import './module/recommend.dart';
// tab内容模块
// import './module/subscribe.dart';
class VideoPage extends StatefulWidget {
const VideoPage({super.key});
@ -137,12 +131,18 @@ class _VideoPageState extends State<VideoPage> with SingleTickerProviderStateMix
),
onPressed: () {
print('当前tab索引:${videoModuleController.videoTabIndex.value}');
if(videoModuleController.videoTabIndex.value == 0){
AttentionModule.pauseVideo();
}else if(videoModuleController.videoTabIndex.value ==1){
if (videoModuleController.videoTabIndex.value == 0) {
AttentionModule.playVideo();
FriendModule.pauseVideo();
}else if(videoModuleController.videoTabIndex.value ==2){
RecommendModule.pauseVideo();
} else if (videoModuleController.videoTabIndex.value == 1) {
AttentionModule.pauseVideo();
FriendModule.playVideo();
RecommendModule.pauseVideo();
} else if (videoModuleController.videoTabIndex.value == 2) {
AttentionModule.pauseVideo();
FriendModule.pauseVideo();
RecommendModule.playVideo();
}
Get.toNamed('/search');
},
@ -159,15 +159,15 @@ class _VideoPageState extends State<VideoPage> with SingleTickerProviderStateMix
// tab
if (index == 0) {
AttentionModule.playVideo();
// FriendModule.pauseVideo();
FriendModule.pauseVideo();
RecommendModule.pauseVideo();
} else if (index == 1) {
AttentionModule.pauseVideo();
// FriendModule.playVideo();
FriendModule.playVideo();
RecommendModule.pauseVideo();
} else if (index == 2) {
AttentionModule.pauseVideo();
// FriendModule.pauseVideo();
FriendModule.pauseVideo();
RecommendModule.playVideo();
}
videoModuleController.updateVideoTabIndex(index);

View File

@ -295,8 +295,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: [
@ -327,7 +327,7 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
GestureDetector(
onTap: () {
setState(() {
replyingCommentId = comment['id'];
replyingCommentId = comment['commentId'];
replyingCommentUser = comment['commentUserNickname'] ?? '未知用户';
});
},
@ -355,10 +355,10 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
GestureDetector(
onTap: () {
setState(() {
expandedReplies[comment['id']] = !isExpanded;
if (expandedReplies[comment['id']] == true &&
(replyData[comment['id']] == null || replyData[comment['id']]!.isEmpty)) {
fetchReplies(comment['id'], false);
expandedReplies[comment['commentId']] = !isExpanded;
if (expandedReplies[comment['commentId']] == true &&
(replyData[comment['commentId']] == null || replyData[comment['commentId']]!.isEmpty)) {
fetchReplies(comment['commentId'], false);
}
});
},
@ -419,7 +419,7 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
GestureDetector(
onTap: () {
setState(() {
replyingCommentId = comment['id'];
replyingCommentId = comment['commentId'];
replyingCommentUser = reply['commentUserNickname'] ?? '未知用户';
});
},
@ -453,8 +453,8 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
if (replies.length < comment['childCount'])
Center(
child: TextButton(
onPressed: () => fetchReplies(comment['id'], true),
child: isLoadingReplies[comment['id']] == true ? CircularProgressIndicator() : Text('加载更多回复'),
onPressed: () => fetchReplies(comment['commentId'], true),
child: isLoadingReplies[comment['commentId']] == true ? CircularProgressIndicator() : Text('加载更多回复'),
),
),
],
@ -557,7 +557,7 @@ class _AttentionModuleState extends State<AttentionModule> {
// controller
late PageController pageController = PageController(
initialPage: videoModuleController.videoPlayIndex.value,
initialPage: videoModuleController.videoPlayIndexFoucs.value,
viewportFraction: 1.0,
);
@ -656,7 +656,7 @@ class _AttentionModuleState extends State<AttentionModule> {
page = 1;
isLoadingMore = false;
videoList.clear();
videoModuleController.updateVideoPlayIndex(0);
videoModuleController.updateVideoPlayIndex2(0);
sliderValue = 0.0;
sliderDraging = false;
position = Duration.zero;
@ -708,7 +708,7 @@ class _AttentionModuleState extends State<AttentionModule> {
//
player.open(
Media(
videoList[videoModuleController.videoPlayIndex.value]['url'],
videoList[videoModuleController.videoPlayIndexFoucs.value]['url'],
),
play: false);
player.setPlaylistMode(PlaylistMode.loop); // ;
@ -771,7 +771,8 @@ class _AttentionModuleState extends State<AttentionModule> {
),
context: context,
builder: (context) {
return CommentBottomSheet(
return SafeArea(
child: CommentBottomSheet(
videoId: videoId,
onCommentCountChanged: (newCount) {
//
@ -780,7 +781,9 @@ class _AttentionModuleState extends State<AttentionModule> {
videoList[index]['commentsCounts'] = newCount;
}
});
});
},
),
);
},
);
}
@ -902,9 +905,9 @@ class _AttentionModuleState extends State<AttentionModule> {
void handleShareClick(int index) {
print("分享项 $index 被点击");
final videoId = videoList[videoModuleController.videoPlayIndex.value]['id'];
final videoUrl = videoList[videoModuleController.videoPlayIndex.value]['url'];
final description = videoList[videoModuleController.videoPlayIndex.value]['title'] ?? '快来看看这个视频';
final videoId = videoList[videoModuleController.videoPlayIndexFoucs.value]['id'];
final videoUrl = videoList[videoModuleController.videoPlayIndexFoucs.value]['url'];
final description = videoList[videoModuleController.videoPlayIndexFoucs.value]['title'] ?? '快来看看这个视频';
logger.i('分享链接地址----------------: ${ShareType.video.name}?id=$videoId');
if (index == 0) {
//
@ -944,13 +947,9 @@ class _AttentionModuleState extends State<AttentionModule> {
try {
//
String? toastId; // toast的ID便
var status = await Permissions.requestStoragePermission();
var status = await Permissions.requestStoragePermission(title: '文件存储使用说明', content: '用于下载视频');
if (!status) {
MyToast().tip(
title: '需要存储权限才能下载视频',
position: 'center',
type: 'success',
);
Permissions.showPermissionDialog('存储');
return;
}
await DownloadManager.downloadFile(
@ -983,10 +982,10 @@ class _AttentionModuleState extends State<AttentionModule> {
void handlCoverClick(V2TimConversation conv) async {
// VideoMsg,
final userId = conv.userID;
final String url = videoList[videoModuleController.videoPlayIndex.value]['url'];
final img = videoList[videoModuleController.videoPlayIndex.value]['firstFrameImg'];
final width = videoList[videoModuleController.videoPlayIndex.value]['width'];
final height = videoList[videoModuleController.videoPlayIndex.value]['height'];
final String url = videoList[videoModuleController.videoPlayIndexFoucs.value]['url'];
final img = videoList[videoModuleController.videoPlayIndexFoucs.value]['firstFrameImg'];
final width = videoList[videoModuleController.videoPlayIndexFoucs.value]['width'];
final height = videoList[videoModuleController.videoPlayIndexFoucs.value]['height'];
final makeJson = jsonEncode({
"width": width,
"height": height,
@ -1050,7 +1049,7 @@ class _AttentionModuleState extends State<AttentionModule> {
scrollDirection: Axis.vertical,
controller: pageController,
onPageChanged: (index) async {
videoModuleController.updateVideoPlayIndex(index);
videoModuleController.updateVideoPlayIndex2(index);
setState(() {
sliderValue = 0.0;
sliderDraging = false;
@ -1082,7 +1081,7 @@ class _AttentionModuleState extends State<AttentionModule> {
child: Stack(
children: [
Visibility(
visible: videoModuleController.videoPlayIndex.value == index && position > Duration.zero,
visible: videoModuleController.videoPlayIndexFoucs.value == index && position > Duration.zero,
child: Video(
controller: videoController,
fit: isHorizontal ? BoxFit.contain : BoxFit.cover,
@ -1090,7 +1089,7 @@ class _AttentionModuleState extends State<AttentionModule> {
),
),
AnimatedOpacity(
opacity: videoModuleController.videoPlayIndex.value == index && position > Duration(milliseconds: 100) ? 0.0 : 1.0,
opacity: videoModuleController.videoPlayIndexFoucs.value == index && position > Duration(milliseconds: 100) ? 0.0 : 1.0,
duration: Duration(milliseconds: 50),
child: Image.network(
videoList[index]['firstFrameImg'] ?? 'https://wuzhongjie.com.cn/download/logo.png',
@ -1144,7 +1143,7 @@ class _AttentionModuleState extends State<AttentionModule> {
onTap: () async {
player.pause();
// Vloger
final vloggerId = videoList[videoModuleController.videoPlayIndex.value]['memberId'];
final vloggerId = videoList[videoModuleController.videoPlayIndexFoucs.value]['memberId'];
final result = await Get.toNamed('/vloger', arguments: {'memberId': vloggerId});
if (result != null) {
//
@ -1164,7 +1163,7 @@ class _AttentionModuleState extends State<AttentionModule> {
),
child: ClipOval(
child: NetworkOrAssetImage(
imageUrl: videoList[index]['commentUserFace'],
imageUrl: videoList[index]['avatar'],
),
),
),
@ -1278,7 +1277,7 @@ class _AttentionModuleState extends State<AttentionModule> {
onTap: () async {
player.pause();
//
final result = await Get.toNamed('/report', arguments: videoList[videoModuleController.videoPlayIndex.value]);
final result = await Get.toNamed('/report', arguments: videoList[videoModuleController.videoPlayIndexFoucs.value]);
if (result != null) {
player.play();
}
@ -1294,13 +1293,20 @@ class _AttentionModuleState extends State<AttentionModule> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (videoModuleController.videoPlayIndexFoucs.value < videoList.length)
Text(
'@${videoList[videoModuleController.videoPlayIndex.value]['nickname'] ?? '未知'}',
'@${videoList[videoModuleController.videoPlayIndexFoucs.value]['nickname'] ?? '未知'}',
style: const TextStyle(color: Colors.white, fontSize: 16.0),
)
else
Text(
'@未知',
style: const TextStyle(color: Colors.white, fontSize: 16.0),
),
if (videoModuleController.videoPlayIndexFoucs.value < videoList.length)
LayoutBuilder(
builder: (context, constraints) {
final text = videoList[videoModuleController.videoPlayIndex.value]['title'] ?? '未知';
final text = videoList[videoModuleController.videoPlayIndexFoucs.value]['title'] ?? '未知';
final span = TextSpan(
text: text,
style: const TextStyle(color: Colors.white, fontSize: 14.0),
@ -1318,8 +1324,8 @@ class _AttentionModuleState extends State<AttentionModule> {
children: [
Text(
text,
maxLines: videoList[videoModuleController.videoPlayIndex.value]['expanded'] ? null : 3,
overflow: videoList[videoModuleController.videoPlayIndex.value]['expanded']
maxLines: videoList[videoModuleController.videoPlayIndexFoucs.value]['expanded'] ? null : 3,
overflow: videoList[videoModuleController.videoPlayIndexFoucs.value]['expanded']
? TextOverflow.visible
: TextOverflow.ellipsis,
style: const TextStyle(color: Colors.white, fontSize: 14.0),
@ -1330,12 +1336,12 @@ class _AttentionModuleState extends State<AttentionModule> {
child: GestureDetector(
onTap: () {
setState(() {
videoList[videoModuleController.videoPlayIndex.value]['expanded'] =
!videoList[videoModuleController.videoPlayIndex.value]['expanded'];
videoList[videoModuleController.videoPlayIndexFoucs.value]['expanded'] =
!videoList[videoModuleController.videoPlayIndexFoucs.value]['expanded'];
});
},
child: Text(
videoList[videoModuleController.videoPlayIndex.value]['expanded'] ? '收起' : '展开更多',
videoList[videoModuleController.videoPlayIndexFoucs.value]['expanded'] ? '收起' : '展开更多',
textAlign: TextAlign.right,
style: const TextStyle(
color: Colors.white,
@ -1348,7 +1354,9 @@ class _AttentionModuleState extends State<AttentionModule> {
],
);
},
),
)
else
SizedBox()
],
)),
Positioned(
@ -1356,7 +1364,7 @@ class _AttentionModuleState extends State<AttentionModule> {
left: 6.0,
right: 6.0,
child: Visibility(
visible: videoModuleController.videoPlayIndex.value == index && position > Duration.zero,
visible: videoModuleController.videoPlayIndexFoucs.value == index && position > Duration.zero,
child: Listener(
child: SliderTheme(
data: SliderThemeData(

View File

@ -296,8 +296,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,7 +328,7 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
GestureDetector(
onTap: () {
setState(() {
replyingCommentId = comment['id'];
replyingCommentId = comment['commentId'];
replyingCommentUser = comment['commentUserNickname'] ?? '未知用户';
});
},
@ -356,10 +356,10 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
GestureDetector(
onTap: () {
setState(() {
expandedReplies[comment['id']] = !isExpanded;
if (expandedReplies[comment['id']] == true &&
(replyData[comment['id']] == null || replyData[comment['id']]!.isEmpty)) {
fetchReplies(comment['id'], false);
expandedReplies[comment['commentId']] = !isExpanded;
if (expandedReplies[comment['commentId']] == true &&
(replyData[comment['commentId']] == null || replyData[comment['commentId']]!.isEmpty)) {
fetchReplies(comment['commentId'], false);
}
});
},
@ -420,7 +420,7 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
GestureDetector(
onTap: () {
setState(() {
replyingCommentId = comment['id'];
replyingCommentId = comment['commentId'];
replyingCommentUser = reply['commentUserNickname'] ?? '未知用户';
});
},
@ -454,8 +454,8 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
if (replies.length < comment['childCount'])
Center(
child: TextButton(
onPressed: () => fetchReplies(comment['id'], true),
child: isLoadingReplies[comment['id']] == true ? CircularProgressIndicator() : Text('加载更多回复'),
onPressed: () => fetchReplies(comment['commentId'], true),
child: isLoadingReplies[comment['commentId']] == true ? CircularProgressIndicator() : Text('加载更多回复'),
),
),
],
@ -558,7 +558,7 @@ class _FriendModuleState extends State<FriendModule> {
// controller
late PageController pageController = PageController(
initialPage: videoModuleController.videoPlayIndex.value,
initialPage: videoModuleController.videoPlayFriend.value,
viewportFraction: 1.0,
);
@ -657,7 +657,7 @@ class _FriendModuleState extends State<FriendModule> {
page = 1;
isLoadingMore = false;
videoList.clear();
videoModuleController.updateVideoPlayIndex(0);
videoModuleController.updateVideoPlayIndex1(0);
sliderValue = 0.0;
sliderDraging = false;
position = Duration.zero;
@ -709,7 +709,7 @@ class _FriendModuleState extends State<FriendModule> {
//
player.open(
Media(
videoList[videoModuleController.videoPlayIndex.value]['url'],
videoList[videoModuleController.videoPlayFriend.value]['url'],
),
play: false);
player.setPlaylistMode(PlaylistMode.loop); // ;
@ -772,7 +772,8 @@ class _FriendModuleState extends State<FriendModule> {
),
context: context,
builder: (context) {
return CommentBottomSheet(
return SafeArea(
child: CommentBottomSheet(
videoId: videoId,
onCommentCountChanged: (newCount) {
//
@ -781,7 +782,9 @@ class _FriendModuleState extends State<FriendModule> {
videoList[index]['commentsCounts'] = newCount;
}
});
});
},
),
);
},
);
}
@ -902,9 +905,9 @@ class _FriendModuleState extends State<FriendModule> {
void handleShareClick(int index) {
print("分享项 $index 被点击");
final videoId = videoList[videoModuleController.videoPlayIndex.value]['id'];
final videoUrl = videoList[videoModuleController.videoPlayIndex.value]['url'];
final description = videoList[videoModuleController.videoPlayIndex.value]['title'] ?? '快来看看这个视频';
final videoId = videoList[videoModuleController.videoPlayFriend.value]['id'];
final videoUrl = videoList[videoModuleController.videoPlayFriend.value]['url'];
final description = videoList[videoModuleController.videoPlayFriend.value]['title'] ?? '快来看看这个视频';
logger.i('分享链接地址----------------: ${ShareType.video.name}?id=$videoId');
if (index == 0) {
//
@ -944,13 +947,9 @@ class _FriendModuleState extends State<FriendModule> {
try {
//
String? toastId; // toast的ID便
var status = await Permissions.requestStoragePermission();
var status = await Permissions.requestStoragePermission(title: '文件存储使用说明', content: '用于下载视频');
if (!status) {
MyToast().tip(
title: '需要存储权限才能下载视频',
position: 'center',
type: 'success',
);
Permissions.showPermissionDialog('存储');
return;
}
await DownloadManager.downloadFile(
@ -983,7 +982,7 @@ class _FriendModuleState extends State<FriendModule> {
void handlCoverClick(V2TimConversation conv) async {
// VideoMsg,
final userId = conv.userID;
final currentVideo = videoList[videoModuleController.videoPlayIndex.value];
final currentVideo = videoList[videoModuleController.videoPlayFriend.value];
logger.w(currentVideo);
final img = (currentVideo['cover'] != null && currentVideo['cover'].toString().isNotEmpty) ? currentVideo['cover'] : currentVideo['firstFrameImg'];
final url = currentVideo['url'];
@ -1055,7 +1054,7 @@ class _FriendModuleState extends State<FriendModule> {
scrollDirection: Axis.vertical,
controller: pageController,
onPageChanged: (index) async {
videoModuleController.updateVideoPlayIndex(index);
videoModuleController.updateVideoPlayIndex1(index);
setState(() {
sliderValue = 0.0;
sliderDraging = false;
@ -1087,7 +1086,7 @@ class _FriendModuleState extends State<FriendModule> {
child: Stack(
children: [
Visibility(
visible: videoModuleController.videoPlayIndex.value == index && position > Duration.zero,
visible: videoModuleController.videoPlayFriend.value == index && position > Duration.zero,
child: Video(
controller: videoController,
fit: isHorizontal ? BoxFit.contain : BoxFit.cover,
@ -1095,7 +1094,7 @@ class _FriendModuleState extends State<FriendModule> {
),
),
AnimatedOpacity(
opacity: videoModuleController.videoPlayIndex.value == index && position > Duration(milliseconds: 100) ? 0.0 : 1.0,
opacity: videoModuleController.videoPlayFriend.value == index && position > Duration(milliseconds: 100) ? 0.0 : 1.0,
duration: Duration(milliseconds: 50),
child: Image.network(
videoList[index]['firstFrameImg'] ?? 'https://wuzhongjie.com.cn/download/logo.png',
@ -1149,7 +1148,7 @@ class _FriendModuleState extends State<FriendModule> {
onTap: () async {
player.pause();
// Vloger
final vloggerId = videoList[videoModuleController.videoPlayIndex.value]['memberId'];
final vloggerId = videoList[videoModuleController.videoPlayFriend.value]['memberId'];
final result = await Get.toNamed('/vloger', arguments: {'memberId': vloggerId});
if (result != null) {
//
@ -1169,7 +1168,7 @@ class _FriendModuleState extends State<FriendModule> {
),
child: ClipOval(
child: NetworkOrAssetImage(
imageUrl: videoList[index]['commentUserFace'],
imageUrl: videoList[index]['avatar'],
),
),
),
@ -1283,7 +1282,7 @@ class _FriendModuleState extends State<FriendModule> {
onTap: () async {
player.pause();
//
final result = await Get.toNamed('/report', arguments: videoList[videoModuleController.videoPlayIndex.value]);
final result = await Get.toNamed('/report', arguments: videoList[videoModuleController.videoPlayFriend.value]);
if (result != null) {
player.play();
}
@ -1299,13 +1298,21 @@ class _FriendModuleState extends State<FriendModule> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (videoModuleController.videoPlayFriend.value < videoList.length)
Text(
'@${videoList[videoModuleController.videoPlayIndex.value]['nickname'] ?? '未知'}',
'@${videoList[videoModuleController.videoPlayFriend.value]['nickname'] ?? '未知'}',
style: const TextStyle(color: Colors.white, fontSize: 16.0),
)
else
Text(
'@未知',
style: const TextStyle(color: Colors.white, fontSize: 16.0),
),
// LayoutBuilder
if (videoModuleController.videoPlayFriend.value < videoList.length)
LayoutBuilder(
builder: (context, constraints) {
final text = videoList[videoModuleController.videoPlayIndex.value]['title'] ?? '未知';
final text = videoList[videoModuleController.videoPlayFriend.value]['title'] ?? '未知';
final span = TextSpan(
text: text,
style: const TextStyle(color: Colors.white, fontSize: 14.0),
@ -1323,8 +1330,8 @@ class _FriendModuleState extends State<FriendModule> {
children: [
Text(
text,
maxLines: videoList[videoModuleController.videoPlayIndex.value]['expanded'] ? null : 3,
overflow: videoList[videoModuleController.videoPlayIndex.value]['expanded']
maxLines: videoList[videoModuleController.videoPlayFriend.value]['expanded'] ? null : 3,
overflow: videoList[videoModuleController.videoPlayFriend.value]['expanded']
? TextOverflow.visible
: TextOverflow.ellipsis,
style: const TextStyle(color: Colors.white, fontSize: 14.0),
@ -1335,12 +1342,12 @@ class _FriendModuleState extends State<FriendModule> {
child: GestureDetector(
onTap: () {
setState(() {
videoList[videoModuleController.videoPlayIndex.value]['expanded'] =
!videoList[videoModuleController.videoPlayIndex.value]['expanded'];
videoList[videoModuleController.videoPlayFriend.value]['expanded'] =
!videoList[videoModuleController.videoPlayFriend.value]['expanded'];
});
},
child: Text(
videoList[videoModuleController.videoPlayIndex.value]['expanded'] ? '收起' : '展开更多',
videoList[videoModuleController.videoPlayFriend.value]['expanded'] ? '收起' : '展开更多',
textAlign: TextAlign.right,
style: const TextStyle(
color: Colors.white,
@ -1353,7 +1360,9 @@ class _FriendModuleState extends State<FriendModule> {
],
);
},
),
)
else
SizedBox()
],
)),
Positioned(
@ -1361,7 +1370,7 @@ class _FriendModuleState extends State<FriendModule> {
left: 6.0,
right: 6.0,
child: Visibility(
visible: videoModuleController.videoPlayIndex.value == index && position > Duration.zero,
visible: videoModuleController.videoPlayFriend.value == index && position > Duration.zero,
child: Listener(
child: SliderTheme(
data: SliderThemeData(

View File

@ -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,7 +20,9 @@ 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/network_utils.dart';
import 'package:loopin/utils/permissions.dart';
import 'package:loopin/utils/wxsdk.dart';
import 'package:media_kit/media_kit.dart';
@ -50,6 +51,10 @@ class RecommendModule extends StatefulWidget {
_player?.play();
}
static void playDispose() {
_player?.dispose();
}
@override
State<RecommendModule> createState() => _RecommendModuleState();
}
@ -298,8 +303,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 +333,12 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
),
SizedBox(width: 20.0),
GestureDetector(
//
onTap: () {
setState(() {
replyingCommentId = comment['id'];
logger.e(comment);
replyingCommentId = comment['commentId'];
replyingCommentUser = comment['commentUserNickname'] ?? '未知用户';
});
},
@ -357,11 +365,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);
}
});
},
@ -422,7 +432,7 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
GestureDetector(
onTap: () {
setState(() {
replyingCommentId = comment['id'];
replyingCommentId = comment['commentId'];
replyingCommentUser = reply['commentUserNickname'] ?? '未知用户';
});
},
@ -456,8 +466,8 @@ class _CommentBottomSheetState extends State<CommentBottomSheet> {
if (replies.length < comment['childCount'])
Center(
child: TextButton(
onPressed: () => fetchReplies(comment['id'], true),
child: isLoadingReplies[comment['id']] == true ? CircularProgressIndicator() : Text('加载更多回复'),
onPressed: () => fetchReplies(comment['commentId'], true),
child: isLoadingReplies[comment['commentId']] == true ? CircularProgressIndicator() : Text('加载更多回复'),
),
),
],
@ -570,7 +580,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; //
@ -595,6 +605,8 @@ class _RecommendModuleState extends State<RecommendModule> {
{'icon': 'assets/images/share-download.png', 'label': '下载'},
];
final NetworkUtils networkUtils = NetworkUtils();
@override
void initState() {
super.initState();
@ -604,9 +616,31 @@ class _RecommendModuleState extends State<RecommendModule> {
videoModuleController.clearNeedRefresh();
}
});
RecommendModule.setPlayer(player);
//
fetchVideoList();
//
ever(
networkUtils.isConnected,
(status) {
logger.e('当前网络状态:$status');
if (status) {
fetchVideoList();
}
},
);
// final NetworkUtils networkUtils = NetworkUtils();
// networkUtils.initialize(
// onStatusChange: (status) {
// //
// logger.e('当前网络状态:$status');
// if (!status) {
// //
// fetchVideoList();
// }
// },
// );
}
@override
@ -709,7 +743,9 @@ class _RecommendModuleState extends State<RecommendModule> {
Media(
videoList[videoModuleController.videoPlayIndex.value]['url'],
),
play: false);
play: false,
);
player.setPlaylistMode(PlaylistMode.loop); // ;
//
@ -734,6 +770,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 +784,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 +808,8 @@ class _RecommendModuleState extends State<RecommendModule> {
),
context: context,
builder: (context) {
return CommentBottomSheet(
return SafeArea(
child: CommentBottomSheet(
videoId: videoId,
onCommentCountChanged: (newCount) {
//
@ -779,7 +818,9 @@ class _RecommendModuleState extends State<RecommendModule> {
videoList[index]['commentsCounts'] = newCount;
}
});
});
},
),
);
},
);
}
@ -943,13 +984,9 @@ class _RecommendModuleState extends State<RecommendModule> {
try {
//
String? toastId; // toast的ID便
var status = await Permissions.requestStoragePermission();
var status = await Permissions.requestStoragePermission(title: '文件存储使用说明', content: '用于下载视频');
if (!status) {
MyToast().tip(
title: '需要存储权限才能下载视频',
position: 'center',
type: 'success',
);
Permissions.showPermissionDialog('存储');
return;
}
await DownloadManager.downloadFile(
@ -1132,9 +1169,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) {
//
@ -1154,13 +1196,14 @@ class _RecommendModuleState extends State<RecommendModule> {
),
child: ClipOval(
child: NetworkOrAssetImage(
imageUrl: videoList[index]['commentUserFace'],
imageUrl: videoList[index]['avatar'],
),
),
),
),
),
),
//
Positioned(
bottom: 0,
left: 15.0,
@ -1184,6 +1227,12 @@ class _RecommendModuleState extends State<RecommendModule> {
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) {
@ -1300,10 +1349,16 @@ class _RecommendModuleState extends State<RecommendModule> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (videoModuleController.videoPlayIndex.value < videoList.length)
Text(
'@${videoList[videoModuleController.videoPlayIndex.value]['nickname'] ?? '未知'}',
style: const TextStyle(color: Colors.white, fontSize: 16.0),
) else
Text(
'@未知',
style: const TextStyle(color: Colors.white, fontSize: 16.0),
),
if (videoModuleController.videoPlayIndex.value < videoList.length)
LayoutBuilder(
builder: (context, constraints) {
final text = videoList[videoModuleController.videoPlayIndex.value]['title'] ?? '未知';
@ -1353,7 +1408,8 @@ class _RecommendModuleState extends State<RecommendModule> {
],
);
},
),
)else
SizedBox()
],
)),
Positioned(

View File

@ -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(),

View File

@ -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),
@ -109,13 +111,13 @@ class HttpConfig {
},
onError: (e, handler) {
//
Get.snackbar(
'网络异常',
e.message ?? '未知错误',
backgroundColor: Colors.red.withAlpha(230),
colorText: Colors.white,
icon: const Icon(Icons.error_outline, color: Colors.white),
);
// Get.snackbar(
// '网络异常',
// '网络连接失败',
// backgroundColor: Colors.red.withAlpha(230),
// colorText: Colors.white,
// icon: const Icon(Icons.error_outline, color: Colors.white),
// );
handler.next(e);
},
),

View File

@ -17,7 +17,9 @@ class UpgradeDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Dialog(
return PopScope(
canPop: !force, //
child: Dialog(
insetPadding: EdgeInsets.all(30),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
@ -52,6 +54,7 @@ class UpgradeDialog extends StatelessWidget {
],
),
),
),
);
}
}

View File

@ -22,15 +22,16 @@ class UpgradeService {
});
if (!state.mounted) return;
logger.w(res);
logger.e('版本:$res');
final result = res['data']['records'] as List;
if (result.isEmpty) return;
final data = result.first;
final currentVersion = info.buildNumber;
if (currentVersion != data['versionCode']) {
if (int.parse(data['versionCode'] ?? 0) > int.parse(currentVersion)) {
//
// 0 false非强制 0 true强制
final bool force = (data['isForceUpdate'] ?? 0) != 0;
// final bool force = (data['isForceUpdate'] ?? 0) != 0;
final bool force = true;
//
showDialog(
context: state.context,

View 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;
}
}

View File

@ -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 {

View File

@ -243,4 +243,22 @@ class Utils {
static bool hasRole(int roleValue, int targetRole) {
return roleValue.toString().contains(targetRole.toString());
}
///
static int calcTime(String createTimeStr, {int validMinutes = 30}) {
try {
// T DateTime.parse
String isoTimeStr = createTimeStr.replaceFirst(' ', 'T');
DateTime createTime = DateTime.parse(isoTimeStr);
DateTime now = DateTime.now();
int totalSeconds = validMinutes * 60;
int elapsedSeconds = now.difference(createTime).inSeconds;
int remainingSeconds = totalSeconds - elapsedSeconds;
return remainingSeconds > 0 ? remainingSeconds : 0;
} catch (e) {
return 0;
}
}
}

View File

@ -25,8 +25,17 @@ class LifecycleHandler with WidgetsBindingObserver {
logger.i("App 进入后台 (paused)");
break;
case AppLifecycleState.detached:
//
isInForeground = false;
logger.i("App 被分离 (detached)");
// if (Get.isRegistered<VideoModuleController>()) {
// final videoModuleController = Get.find<VideoModuleController>();
// logger.w(videoModuleController.videoTabIndex.value);
// logger.w(videoModuleController.videoPlayFriend.value);
// logger.w(videoModuleController.videoPlayIndex.value);
// RecommendModule.playDispose();
// }
break;
case AppLifecycleState.hidden:
isInForeground = false;

View File

@ -0,0 +1,58 @@
import 'dart:async';
import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:get/get.dart';
typedef NetworkStatusCallback = void Function(bool isConnected);
class NetworkUtils {
static final NetworkUtils _instance = NetworkUtils._internal();
factory NetworkUtils() => _instance;
NetworkUtils._internal();
final Connectivity _connectivity = Connectivity();
StreamSubscription<List<ConnectivityResult>>? _subscription;
/// RxBool
final RxBool _isConnected = false.obs;
///
void initialize({NetworkStatusCallback? onStatusChange}) async {
//
_isConnected.value = await checkNetworkAvailable();
onStatusChange?.call(_isConnected.value);
//
_subscription = _connectivity.onConnectivityChanged.listen((result) async {
bool newStatus = await checkNetworkAvailable();
if (newStatus != _isConnected.value) {
_isConnected.value = newStatus;
onStatusChange?.call(_isConnected.value);
}
});
}
///
Future<bool> checkNetworkAvailable() async {
final connectivityResult = await _connectivity.checkConnectivity();
if (connectivityResult.first == ConnectivityResult.none) return false;
try {
final result = await InternetAddress.lookup('wuzhongjie.com.cn');
return result.isNotEmpty && result[0].rawAddress.isNotEmpty;
} on SocketException catch (_) {
return false;
}
}
/// 访
RxBool get isConnected => _isConnected;
///
void dispose() {
_subscription?.cancel();
}
}

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:loopin/IM/im_service.dart';
import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/models/conversation_type.dart';
import 'package:loopin/styles/index.dart';
import 'package:loopin/utils/parse_message_summary.dart';
import 'package:shirne_dialog/shirne_dialog.dart';
@ -20,6 +21,8 @@ class NotificationBanner {
name = gpInfo?.groupName ?? "未知群名";
avatar = gpInfo?.faceUrl ?? "";
} else {
logger.e('获取群名称失败');
logger.e(msg.toJson());
name = '获取群名称失败';
}
} else {
@ -71,18 +74,25 @@ class NotificationBanner {
),
onTap: (_) async {
Get.closeCurrentSnackbar();
String? conversationID;
if (msg.groupID != null && msg.groupID!.isNotEmpty) {
conversationID = 'group_${msg.groupID}';
} else if (msg.userID != null && msg.userID!.isNotEmpty) {
conversationID = 'c2c_${msg.userID}';
//
bool isGroup = msg.groupID != null && msg.groupID!.isNotEmpty;
String id = isGroup ? (msg.groupID ?? '') : (msg.userID ?? '');
String conversationID = isGroup ? 'group_$id' : 'c2c_$id';
String? router;
//
final cRes = await ImService.instance.getConversation(conversationID: conversationID);
if (!isGroup) {
// ,
router = conversationTypeFromString(msg.userID); //userID
}
final cRes = await ImService.instance.getConversation(conversationID: conversationID!);
if (cRes.success) {
if (msg.userID != null) {
Get.toNamed('/chat', arguments: cRes.data);
} else if (msg.groupID != null) {
if (router != null) {
//
Get.toNamed('/$router', arguments: cRes.data);
} else if (isGroup) {
Get.toNamed('/chatGroup', arguments: cRes.data);
} else {
Get.toNamed('/chat', arguments: cRes.data);
}
} else {
MyDialog.toast(

View File

@ -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('解析elementaciton=$sum,${elment?.desc ?? 'summary_error'}');
try {
switch (sum) {
case SummaryType.hongbao:
@ -234,6 +233,8 @@ String _parseCustomMessage(V2TimMessage? msg) {
///广
return elment!.desc!;
// interaction required key={type} value={interactionComment}
case NotifyMessageTypeConstants.interactionComment:
//
@ -275,22 +276,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]

View File

@ -1,15 +1,41 @@
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:loopin/IM/im_service.dart';
import 'package:loopin/components/my_confirm.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:photo_manager/photo_manager.dart';
class Permissions {
// 访
static Future<bool> requestVideoPermission() async {
///
static Future<bool> isHuaweiDevice() async {
if (Platform.isAndroid) {
final deviceInfoPlugin = DeviceInfoPlugin();
final androidInfo = await deviceInfoPlugin.androidInfo;
//
return androidInfo.brand.toLowerCase().contains('huawei') || androidInfo.manufacturer.toLowerCase().contains('huawei');
}
return false;
}
// 访
static Future<bool> requestVideoPermission({String? title, String? content}) async {
if (Platform.isAndroid) {
SnackbarController? ctl;
final isHw = await isHuaweiDevice();
if (isHw) {
ctl = Get.snackbar(
title ?? '相册权限使用说明:',
content ?? '我们需要读取您的手机相册,以便您从相册中选择文件',
duration: Duration(days: 1),
backgroundColor: Colors.red.withAlpha(230),
colorText: Colors.white,
icon: const Icon(Icons.error_outline, color: Colors.white),
);
}
final deviceInfoPlugin = DeviceInfoPlugin();
final androidInfo = await deviceInfoPlugin.androidInfo;
final sdkInt = androidInfo.version.sdkInt;
@ -17,23 +43,42 @@ class Permissions {
if (sdkInt >= 33) {
// Android 13
final status = await Permission.videos.request();
if (ctl != null) {
ctl.close();
}
return status.isGranted;
} else {
// Android 12
final status = await Permission.storage.request();
if (ctl != null) {
ctl.close();
}
return status.isGranted;
}
} else if (Platform.isIOS) {
final status = await Permission.photos.request();
return status.isGranted || status.isLimited;
// return status.isGranted || status.isLimited;
return handleStatus(status, isAndroid: false);
} else {
return false;
}
}
//
static Future<bool> requestPhotoPermission() async {
static Future<bool> requestPhotoPermission({String? title, String? content}) async {
if (Platform.isAndroid) {
final isHw = await isHuaweiDevice();
SnackbarController? ctl;
if (isHw) {
ctl = Get.snackbar(
title ?? '相册权限使用说明:',
content ?? '我们需要读取您的手机相册,以便您从相册中选择文件',
duration: Duration(days: 1),
backgroundColor: Colors.red.withAlpha(230),
colorText: Colors.white,
icon: const Icon(Icons.error_outline, color: Colors.white),
);
}
final deviceInfoPlugin = DeviceInfoPlugin();
final androidInfo = await deviceInfoPlugin.androidInfo;
final sdkInt = androidInfo.version.sdkInt;
@ -41,11 +86,17 @@ class Permissions {
if (sdkInt >= 33) {
// Android 13
final status = await Permission.photos.request();
if (ctl != null) {
ctl.close();
}
// return status.isGranted;
return handleStatus(status, isAndroid: true);
} else {
// Android 12
final status = await Permission.storage.request();
if (ctl != null) {
ctl.close();
}
// return status.isGranted;
return handleStatus(status, isAndroid: true);
}
@ -59,44 +110,86 @@ class Permissions {
}
}
//
static Future<bool> requestCameraPermission() async {
return await _checkAndRequest(Permission.camera);
//
static Future<bool> requestCameraPermission({String? title, String? content}) async {
return await _checkAndRequest(Permission.camera, title: title, content: content);
}
//
static Future<bool> requestMicrophonePermission() async {
return await _checkAndRequest(Permission.microphone);
static Future<bool> requestMicrophonePermission({String? title, String? content}) async {
return await _checkAndRequest(Permission.microphone, title: title, content: content);
}
//
static Future<bool> requestStoragePermission() async {
return await _checkAndRequest(Permission.storage);
static Future<bool> requestStoragePermission({String? title, String? content}) async {
return await _checkAndRequest(Permission.storage, title: title, content: content);
}
//
static Future<bool> _checkAndRequest(Permission permission) async {
final status = await permission.status;
if (status.isGranted) return true;
static Future<bool> _checkAndRequest(Permission permission, {String? title, String? content}) async {
if (Platform.isAndroid) {
final isHw = await isHuaweiDevice();
SnackbarController? ctl;
if (isHw) {
ctl = Get.snackbar(
title ?? '麦克风权限使用说明:',
content ?? '用于发送语音消息',
duration: Duration(days: 1),
backgroundColor: Colors.red.withAlpha(230),
colorText: Colors.white,
icon: const Icon(Icons.error_outline, color: Colors.white),
);
}
final result = await permission.request();
if (result.isGranted) return true;
if (result.isPermanentlyDenied) {
//
return false;
if (ctl != null) {
ctl.close();
}
return handleStatus(result, isAndroid: true);
} else {
//
Get.snackbar('权限请求失败', '无法访问,请授权对应权限后重试');
final result = await permission.request();
return handleStatus(result, isAndroid: false);
}
return false;
// final status = await permission.status;
// if (status.isGranted) {
// //
// if (ctl != null) {
// ctl.close();
// }
// return true;
// }
// final result = await permission.request();
// if (result.isGranted) {
// if (ctl != null) {
// ctl.close();
// }
// return true;
// }
// if (result.isPermanentlyDenied) {
// //
// if (ctl != null) {
// ctl.close();
// }
// return false;
// } else {
// if (ctl != null) {
// ctl.close();
// }
// //
// Get.snackbar('权限请求失败', '无法访问,请授权对应权限后重试');
// }
// return false;
}
///
static bool handleStatus(PermissionStatus status, {required bool isAndroid}) {
static Future<bool> handleStatus(PermissionStatus status, {required bool isAndroid, SnackbarController? ctl}) async {
logger.w("当前权限状态 = $status");
logger.e(status.isPermanentlyDenied);
if (status.isGranted) {
@ -110,10 +203,22 @@ class Permissions {
return true; // Limited
}
if (status.isPermanentlyDenied) {
// debug permanentlyDenied
// 访 true
logger.w("可能已授权,直接允许访问");
return true;
// ios
// permission_handler在ios下只能检测出limited和granted;permanentlyDenied的问题
final result = await PhotoManager.requestPermissionExtend();
logger.e(result);
switch (result) {
case PermissionState.authorized:
return true; //
case PermissionState.limited:
return true; //
case PermissionState.denied:
return false;
case PermissionState.restricted:
return false;
default:
return false; //
}
}
}

View File

@ -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'); // 广

View 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.ktappdelegate.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;
}
}
}

View File

@ -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);
}

View File

@ -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:
@ -201,6 +233,22 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.4.1"
connectivity_plus:
dependency: "direct main"
description:
name: connectivity_plus
sha256: "33bae12a398f841c6cda09d1064212957265869104c478e5ad51e2fb26c3973c"
url: "https://pub.flutter-io.cn"
source: hosted
version: "7.0.0"
connectivity_plus_platform_interface:
dependency: transitive
description:
name: connectivity_plus_platform_interface
sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.1"
cross_file:
dependency: transitive
description:
@ -529,10 +577,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 +653,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:
@ -973,6 +1029,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.0"
nm:
dependency: transitive
description:
name: nm
sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.5.0"
octo_image:
dependency: transitive
description:
@ -1682,6 +1746,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:

View File

@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 4.1.0+407
version: 4.1.4+416
environment:
sdk: ^3.6.0
@ -59,6 +59,10 @@ dependencies:
media_kit: ^1.1.11
media_kit_video: ^1.2.5
media_kit_libs_video: ^1.0.5
# media_kit: ^1.2.0
# media_kit_video: ^1.3.0
# media_kit_libs_video: ^1.0.6
photo_view: ^0.15.0
shirne_dialog: ^4.8.3
@ -74,7 +78,7 @@ dependencies:
wechat_assets_picker: ^9.5.1
device_info_plus: ^11.5.0
photo_manager: ^3.7.1 #翻译媒体
photo_manager: ^3.7.1 #翻译媒体,权限
flutter_form_builder: ^10.0.1
form_builder_validators: ^11.1.2
geolocator: ^14.0.1
@ -85,7 +89,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 +105,9 @@ 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
connectivity_plus: ^7.0.0
dev_dependencies:
flutter_launcher_icons: ^0.13.1 # 使用最新版本
@ -188,21 +196,28 @@ 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"
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
color_dark: "#ffffff"
image_dark: assets/images/logo/start.png
# iOS 图片显示模式 - 填满屏幕(可能裁剪)
ios_content_mode: scaleAspectFill
# Android 11 及以下图片显示模式 - 填满屏幕
android_gravity: fill
fullscreen: true #隐藏通知栏
android_12:
image: assets/images/logo/logo.png
color: "#000000"
color_dark: "#000000"
image: assets/images/logo/androidlogo.png
icon_background_color: "#000000"
# image_dark: assets/images/logo/start.png
# icon_background_color_dark: "#000000"
image_dark: assets/images/logo/androidlogo.png
icon_background_color_dark: "#000000"
web: false