diff --git a/XinjiangProject/XinjiangProject.xcodeproj/project.pbxproj b/XinjiangProject/XinjiangProject.xcodeproj/project.pbxproj index 687d18b..4264386 100644 --- a/XinjiangProject/XinjiangProject.xcodeproj/project.pbxproj +++ b/XinjiangProject/XinjiangProject.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 188C3FFF3005E07600461C07 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 188C3FFE3005E07600461C07 /* Security.framework */; }; + 188C40013005E08000461C07 /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 188C40003005E08000461C07 /* CoreTelephony.framework */; }; 2A9D6F4901C83B23A09A207B /* libPods-XinjiangProject.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5380821CF956D8001EF91CA7 /* libPods-XinjiangProject.a */; }; /* End PBXBuildFile section */ @@ -31,6 +33,10 @@ 1809E4622E092C710034C8FC /* XinjiangProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XinjiangProject.app; sourceTree = BUILT_PRODUCTS_DIR; }; 1809E47D2E092C740034C8FC /* XinjiangProjectTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XinjiangProjectTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 1809E4872E092C740034C8FC /* XinjiangProjectUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XinjiangProjectUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 188C3FFE3005E07600461C07 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + 188C40003005E08000461C07 /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; }; + 188C40023005E08B00461C07 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + 188C40033005E09100461C07 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; 5380821CF956D8001EF91CA7 /* libPods-XinjiangProject.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-XinjiangProject.a"; sourceTree = BUILT_PRODUCTS_DIR; }; D5F062871D7E5499A760A0A9 /* Pods-XinjiangProject.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-XinjiangProject.release.xcconfig"; path = "Target Support Files/Pods-XinjiangProject/Pods-XinjiangProject.release.xcconfig"; sourceTree = ""; }; F4F935FBD1765B37B37D212D /* Pods-XinjiangProject.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-XinjiangProject.debug.xcconfig"; path = "Target Support Files/Pods-XinjiangProject/Pods-XinjiangProject.debug.xcconfig"; sourceTree = ""; }; @@ -72,6 +78,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 188C40013005E08000461C07 /* CoreTelephony.framework in Frameworks */, + 188C3FFF3005E07600461C07 /* Security.framework in Frameworks */, 2A9D6F4901C83B23A09A207B /* libPods-XinjiangProject.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -118,6 +126,10 @@ 5E0194FB12246C95B3FA5215 /* Frameworks */ = { isa = PBXGroup; children = ( + 188C40033005E09100461C07 /* libc++.tbd */, + 188C40023005E08B00461C07 /* libz.tbd */, + 188C40003005E08000461C07 /* CoreTelephony.framework */, + 188C3FFE3005E07600461C07 /* Security.framework */, 5380821CF956D8001EF91CA7 /* libPods-XinjiangProject.a */, ); name = Frameworks; @@ -374,8 +386,8 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = XinjiangProject/XinjiangProject.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = A7CC5LW224; ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_PREFIX_HEADER = "$(SRCROOT)/XinjiangProject/Supporting Files/PrefixHeader.pch"; @@ -400,7 +412,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.13; + MARKETING_VERSION = 1.0.14; PRODUCT_BUNDLE_IDENTIFIER = com.questionTools.XinjiangProject; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -418,8 +430,8 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = XinjiangProject/XinjiangProject.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = A7CC5LW224; ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_PREFIX_HEADER = "$(SRCROOT)/XinjiangProject/Supporting Files/PrefixHeader.pch"; @@ -444,7 +456,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.13; + MARKETING_VERSION = 1.0.14; PRODUCT_BUNDLE_IDENTIFIER = com.questionTools.XinjiangProject; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; diff --git a/XinjiangProject/XinjiangProject/Define/StatusMacros.h b/XinjiangProject/XinjiangProject/Define/StatusMacros.h index ab81e3b..6db6cc7 100755 --- a/XinjiangProject/XinjiangProject/Define/StatusMacros.h +++ b/XinjiangProject/XinjiangProject/Define/StatusMacros.h @@ -63,11 +63,11 @@ //正式地址 -#define Http @"https://api-jkglpt.iosp.ydpt.tech"// -#define H5DevHost @"api-jkglpt.iosp.ydpt.tech"// -#define IntervenH5 @"https://console-jkglpt.iosp.ydpt.tech" -#define SCHEME @"https" // -#define OtherHttp @"https://api-jkglpt.iosp.ydpt.tech/hb" // +//#define Http @"https://api-jkglpt.iosp.ydpt.tech"// +//#define H5DevHost @"api-jkglpt.iosp.ydpt.tech"// +//#define IntervenH5 @"https://console-jkglpt.iosp.ydpt.tech" +//#define SCHEME @"https" // +//#define OtherHttp @"https://api-jkglpt.iosp.ydpt.tech/hb" // @@ -75,11 +75,11 @@ //#define Http @"http://192.168.1.223"//彭杰 // 最新地址 //#define Http @"http://192.168.1.47"//王昊 -//#define Http @"https://xjxc-api.mcrm.vip:8888"// -//#define H5DevHost @"xjxc-api.mcrm.vip:8888"// -//#define IntervenH5 @"https://xj-admin.mcrm.vip:8888" -//#define SCHEME @"https" // -//#define OtherHttp @"https://gstest.superwx.cn/xj_health" // +#define Http @"https://xj-api.yixiong-tech.com:8081"// +#define H5DevHost @"xjxc-api.mcrm.vip:8888"// +#define IntervenH5 @"http://10.10.10.228:23006/" +#define SCHEME @"https" // +#define OtherHttp @"https://gstest.superwx.cn/xj_health" // #pragma mark - ——————— 健康知识/资讯 ———————— @@ -105,5 +105,8 @@ #define kHealthKnowledgeCategoryPath @"/cms/frontend/category/list" #define kHealthKnowledgeArticleListPath @"/cms/frontend/articleList" +// 急救宣教资源 +#define kFirstAidResourceDetailPath @"/health-emergency/api/emergency/firstAid/resource/resource/queryById" + #endif /* StatusMacros_h */ diff --git a/XinjiangProject/XinjiangProject/Define/ThirdMacros.h b/XinjiangProject/XinjiangProject/Define/ThirdMacros.h index 1f83240..0e04a20 100755 --- a/XinjiangProject/XinjiangProject/Define/ThirdMacros.h +++ b/XinjiangProject/XinjiangProject/Define/ThirdMacros.h @@ -10,9 +10,11 @@ #define ThirdMacros_h //微信 -#define kAppKey_Wechat @"" +#define kAppKey_Wechat @"wxb33cd60beda0a212" -#define kSecret_Wechat @"" +#define kSecret_Wechat @"9de1222d6c32af184538d2ae25d1f5a2" + +#define kUniversalLink_Wechat @"https://www.rk-health.com" #define UMShareAppKey @"" @@ -22,6 +24,7 @@ #define buglyAppid @"" +#define SecretKey @"XJp3EHpcnwReXc1A" //高德key diff --git a/XinjiangProject/XinjiangProject/Manager/NetworkHelpManager/JKCQNetworkingWithCache.h b/XinjiangProject/XinjiangProject/Manager/NetworkHelpManager/JKCQNetworkingWithCache.h index 8d267e5..3edf062 100755 --- a/XinjiangProject/XinjiangProject/Manager/NetworkHelpManager/JKCQNetworkingWithCache.h +++ b/XinjiangProject/XinjiangProject/Manager/NetworkHelpManager/JKCQNetworkingWithCache.h @@ -55,4 +55,7 @@ typedef void (^FileDownloadFail)(int code, NSString * desc); #pragma mark - 表单带参数 - (void)httpPostFormDataRequest:(NSString *)api params:(NSMutableDictionary *)params; + +//外部接口 +- (void)httpOtherPostRequest:(NSString *)api params:(NSMutableDictionary *)params; @end diff --git a/XinjiangProject/XinjiangProject/Manager/NetworkHelpManager/JKCQNetworkingWithCache.m b/XinjiangProject/XinjiangProject/Manager/NetworkHelpManager/JKCQNetworkingWithCache.m index b55af9d..f8cdbfc 100755 --- a/XinjiangProject/XinjiangProject/Manager/NetworkHelpManager/JKCQNetworkingWithCache.m +++ b/XinjiangProject/XinjiangProject/Manager/NetworkHelpManager/JKCQNetworkingWithCache.m @@ -8,7 +8,7 @@ // #import "JKCQNetworkingWithCache.h" - +#import #define DYLog(...) NSLog(__VA_ARGS__) //如果不需要打印数据, 注释掉NSLog @@ -20,8 +20,8 @@ typedef NS_ENUM(NSInteger, RequestType) { RequestTypeMultiUpload,//多个上传 RequestTypeDownload, RequestTypeUpLoadVideo,//上传视频 - RequestTypeParmFormData//参数表单 - + RequestTypeParmFormData,//参数表单 + RequestTypeOterTypePost//外部接口 }; @implementation JKCQNetworkingWithCache @@ -65,6 +65,11 @@ typedef NS_ENUM(NSInteger, RequestType) { [self httpRequestWithUrlStr:api params:params requestType:RequestTypePost isCache:YES cacheKey:api imageKey:nil withData:nil withDataArray:nil]; } +- (void)httpOtherPostRequest:(NSString *)api params:(NSMutableDictionary *)params +{ + [self httpRequestWithUrlStr:api params:params requestType:RequestTypeOterTypePost isCache:NO cacheKey:nil imageKey:nil withData:nil withDataArray:nil]; +} + #pragma mark - 上传文件方法 //上传单张图片 - (void)upLoadDataWithUrlStr:(NSString *)api params:(NSMutableDictionary *)params imageKey:(NSString *)name withData:(NSData *)data @@ -372,8 +377,63 @@ typedef NS_ENUM(NSInteger, RequestType) { [MBProgressHUD hideHUDForView:keyWindow animated:YES]; [EasyTextView showErrorText:@"请求超时"]; } + }]; + }else if(requestType == RequestTypeOterTypePost) {//参数表单上传 + //获取当前时间戳 + + NSDate *date = [NSDate date]; + + NSTimeInterval time = [date timeIntervalSince1970]; + + NSString *timeString = [NSString stringWithFormat:@"%.0f", time]; + + [session.requestSerializer setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"]; + //加密 + NSString * needSign = [NSString stringWithFormat:@"%@%@",timeString,params]; + + NSString * sign = [self signWithTimestamp:timeString body:params secretKey:SecretKey]; + + DLog(@"%@",sign); + + [session POST:url parameters:params headers:@{@"X-Timestamp":timeString,@"X-Sign":sign} progress:^(NSProgress * _Nonnull downloadProgress) { + + } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { + + [weakSelf dealWithResponseObject:responseObject cacheUrl:allUrl]; + + } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { + NSString * statusCode = [error.userInfo objectForKey:@"NSLocalizedDescription"]; + if ([statusCode containsString:@"401"]) { + DLog(@"%@",error.userInfo); + [HQCommonUtils keyedArchiverWithData:nil key:LoginUserInfo]; + KPostNotification(KNotificationLoginStateChange, @NO); + if (![HQCommonUtils isLogin]) { + [EasyTextView showErrorText:@"请您登录"]; + } + else{ + [EasyTextView showErrorText:@"身份信息已过期"]; + } + } + else + { + + NSLog(@"%@",url); + [MBProgressHUD hideHUDForView:[HQCommonUtils getCurrentVC].view animated:YES]; + UIWindow *keyWindow = UIApplication.sharedApplication.windows.firstObject; + for (UIWindow *window in UIApplication.sharedApplication.windows) { + if (window.isKeyWindow) { + keyWindow = window; + break; + } + } + + [MBProgressHUD hideHUDForView:keyWindow animated:YES]; + [EasyTextView showErrorText:@"请求超时"]; + } + + }]; } } @@ -582,5 +642,38 @@ typedef NS_ENUM(NSInteger, RequestType) { self.requestDelegate=nil; } - +#pragma mark:外部加密sign +- (NSString *)signWithTimestamp:(NSString *)timestamp + body:(NSDictionary *)bodyDict + secretKey:(NSString *)secretKey { + + // 1 字典转 JSON + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:bodyDict options:0 error:nil]; + NSString *body = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + + // 2 拼接字符串 + NSString *signString = [NSString stringWithFormat:@"%@%@", timestamp, body]; + + // 3 HMAC-SHA256 + const char *key = [secretKey UTF8String]; + const char *data = [signString UTF8String]; + + unsigned char result[CC_SHA256_DIGEST_LENGTH]; + + CCHmac(kCCHmacAlgSHA256, + key, + strlen(key), + data, + strlen(data), + result); + + // 4 转16进制 + NSMutableString *hash = [NSMutableString string]; + + for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) { + [hash appendFormat:@"%02x", result[i]]; + } + + return hash; +} @end diff --git a/XinjiangProject/XinjiangProject/Manager/NetworkHelpManager/XJPNetAPI.h b/XinjiangProject/XinjiangProject/Manager/NetworkHelpManager/XJPNetAPI.h index 5d9d082..deeb2ec 100755 --- a/XinjiangProject/XinjiangProject/Manager/NetworkHelpManager/XJPNetAPI.h +++ b/XinjiangProject/XinjiangProject/Manager/NetworkHelpManager/XJPNetAPI.h @@ -27,6 +27,22 @@ - (void)GetSoSHomePageResources:(NSMutableDictionary *)pamraDic; - (void)PostEndSoSIMQuestion:(NSMutableDictionary *)pamraDic; - (void)getSoSCarInfoLocationHome:(NSMutableDictionary *)parmDic; +// 急救员 - 搜索专家 +- (void)getSearchExpert:(NSMutableDictionary *)parmDic; +// 急救员 - 搜索员工 +- (void)getSearchEmployee:(NSMutableDictionary *)parmDic; +// 急救员 - 创建群聊(不发起音视频通话) +- (void)getCreateGroupMagFirstAider:(NSMutableDictionary *)parmDic; +// 急救员 - 邀请进群 +- (void)postAddGroupUser:(NSMutableDictionary *)parmDic; +// 急救员 - 移除群成员 +- (void)postRemoveGroupUser:(NSMutableDictionary *)parmDic; +// 急救员 - 群成员列表 +- (void)getGroupMemberList:(NSMutableDictionary *)parmDic; +// 急救员 - 实际群成员列表(经腾讯IM验证,含isSelf字段) +- (void)getActualGroupMemberList:(NSMutableDictionary *)parmDic; +// 急救员 - 宣教资源详情 +- (void)getFirstAidResourceDetail:(NSMutableDictionary *)parmDic; //首页接口 @@ -186,4 +202,6 @@ //解绑手表 - (void)getUnBindWatchDevice:(NSMutableDictionary *)parmDic; +- (void)postPointsUnmberData:(NSMutableDictionary *)parmDic; + @end diff --git a/XinjiangProject/XinjiangProject/Manager/NetworkHelpManager/XJPNetAPI.m b/XinjiangProject/XinjiangProject/Manager/NetworkHelpManager/XJPNetAPI.m index 6d093ad..a424423 100755 --- a/XinjiangProject/XinjiangProject/Manager/NetworkHelpManager/XJPNetAPI.m +++ b/XinjiangProject/XinjiangProject/Manager/NetworkHelpManager/XJPNetAPI.m @@ -64,11 +64,51 @@ - (void)getSoSCarInfoLocationHome:(NSMutableDictionary *)parmDic { - + [httpRequest httpGetRequest:[NSString stringWithFormat:@"%@/medical-center/api/ambulance/nearbyAmbulance",Http] params:parmDic]; } +// 急救员 - 搜索专家 +- (void)getSearchExpert:(NSMutableDictionary *)parmDic { + [httpRequest httpGetRequest:[NSString stringWithFormat:@"%@/health-emergency/api/emergency/order/searchExpert",Http] params:parmDic]; +} + +// 急救员 - 创建群聊(不发起音视频通话) +- (void)getCreateGroupMagFirstAider:(NSMutableDictionary *)parmDic { + [httpRequest httpGetRequest:[NSString stringWithFormat:@"%@/health-emergency/api/emergency/createGroupMagFirstAider",Http] params:parmDic]; +} + +// 急救员 - 搜索员工 +- (void)getSearchEmployee:(NSMutableDictionary *)parmDic { + [httpRequest httpGetRequest:[NSString stringWithFormat:@"%@/health-emergency/api/emergency/order/searchEmployee",Http] params:parmDic]; +} + +// 急救员 - 邀请进群 +- (void)postAddGroupUser:(NSMutableDictionary *)parmDic { + [httpRequest httpPostRequest:[NSString stringWithFormat:@"%@/health-emergency/api/emergency/addGroupUser",Http] params:parmDic]; +} + +// 急救员 - 移除群成员 +- (void)postRemoveGroupUser:(NSMutableDictionary *)parmDic { + [httpRequest httpPostRequest:[NSString stringWithFormat:@"%@/health-emergency/api/emergency/removeGroupUser",Http] params:parmDic]; +} + +// 急救员 - 群成员列表 +- (void)getGroupMemberList:(NSMutableDictionary *)parmDic { + [httpRequest httpGetRequest:[NSString stringWithFormat:@"%@/health-emergency/api/emergency/order/getGroupMemberList",Http] params:parmDic]; +} + +// 急救员 - 实际群成员列表(经腾讯IM验证,含isSelf字段) +- (void)getActualGroupMemberList:(NSMutableDictionary *)parmDic { + [httpRequest httpGetRequest:[NSString stringWithFormat:@"%@/health-emergency/api/emergency/order/getActualGroupMemberList",Http] params:parmDic]; +} + +// 急救员 - 宣教资源详情 +- (void)getFirstAidResourceDetail:(NSMutableDictionary *)parmDic { + [httpRequest httpGetRequest:[NSString stringWithFormat:@"%@/health-emergency/api/emergency/firstAid/resource/resource/queryById",Http] params:parmDic]; +} + - (void)getNearbyHomePageHospitalPoint:(NSMutableDictionary *)parmDic { [httpRequest httpGetRequest:[NSString stringWithFormat:@"%@/medical-center/api/resource/nearbyResource",Http] params:parmDic]; @@ -913,6 +953,7 @@ [httpRequest httpGetRequest:[NSString stringWithFormat:@"%@/health-watch/api/watchData/userWatchDataAll",Http] params:parmDic]; } + - (void)getBindWatchDevice:(NSMutableDictionary *)parmDic { [httpRequest httpGetRequest:[NSString stringWithFormat:@"%@/health-watch/watch/watchDevice/app/bindDevice",Http] params:parmDic]; @@ -922,4 +963,10 @@ [httpRequest httpGetRequest:[NSString stringWithFormat:@"%@/health-watch/watch/watchDevice/app/unbundleDevice",Http] params:parmDic]; } + +//外部接口 +- (void)postPointsUnmberData:(NSMutableDictionary *)parmDic { + + [httpRequest httpOtherPostRequest:[NSString stringWithFormat:@"%@/api/v1/users/point-details",OtherHttp] params:parmDic]; +} @end diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/Info.plist b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/Info.plist new file mode 100644 index 0000000..925fc2f --- /dev/null +++ b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/Info.plist @@ -0,0 +1,44 @@ + + + + + AvailableLibraries + + + BinaryPath + WechatOpenSDK.framework/WechatOpenSDK + LibraryIdentifier + ios-arm64 + LibraryPath + WechatOpenSDK.framework + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + BinaryPath + WechatOpenSDK.framework/WechatOpenSDK + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + WechatOpenSDK.framework + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/PrivacyInfo.xcprivacy b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..79bc9e2 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/PrivacyInfo.xcprivacy @@ -0,0 +1,17 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + + diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/README.txt b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/README.txt new file mode 100644 index 0000000..3f2aab0 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/README.txt @@ -0,0 +1,167 @@ +重要! +SDK2.0.5 +1. 支持模块化集成​:XCFramework 头文件引用改为标准化格式 #import ,解决路径冲突并支持 Swift/ObjC 混合开发 +2. 修复openWXApp偶现失败的问题 + +SDK2.0.4 +1.增加privacy manifest文件 +2.修复跳微信时可能卡顿的问题 + +SDK2.0.2 +1. 优化XCFramework打包方式 + +SDK2.0.1 +1. SDK支持XCFramework + +SDK2.0.0 +1. 分享能力支持内容防篡改校验 + +SDK1.9.9 +1. 授权登录支持关闭自动授权 +2. 分享支持添加签名,防止篡改 + +SDK1.9.7 +1. 适配CocoaPods + +SDK1.9.6 +1. 适配iOS 16,减少读写剪切板 + +SDK1.9.4 +1. 修复授权登录取消/拒绝时state字段没有带回 + +SDK1.9.3 +1. 新增发起二维码支付能力 + +SDK1.9.2 +1. 新增发起企微客服会话能力 + +SDK1.9.1 +1. 音乐视频分享类型增加运营H5字段 + +SDK1.8.9 +1. 增加音乐视频分享类型 + +SDK1.8.8 +1. 增加游戏直播消息类型 + +SDK1.8.7.1 +1. 修复Xcode11以下编译不通过 + +SDK1.8.7 +1. 修复iPadOS,未安装微信的情况下,因为UA问题无法授权登录 +2. 修复未安装微信的情况下, 适配了UIScene的App因为UIAlertView Crash +3. 增加Universal Link检测函数 + +SDK1.8.6.2 +1. 修改包含"UIWebView"字符的类名 + +SDK1.8.6.1 +1.短信授权登录使用的UIWebview切换成WKWebview + +SDK1.8.6 +1. 支持Universal Link拉起微信以及返回App +2. SDK移除MTA库 + +SDK1.8.5 +1. 更换MTA库:取消对剪切板的访问, 防止和其他SDK竞争导致crash +2. NSMutableArray的MTA分类方法改名,减少命名冲突 +3. 不含支付功能版本移除非税支付和医保支付接口 +4. 分享音乐支持填写歌词和高清封面图 + +SDK1.8.4 +1. 调整分享图片大小限制 +2. 新增openBusinessView接口 + +SDK1.8.3 +1. SDK增加调起微信刷卡支付接口 +2. SDK增加小程序订阅消息接口 +3. 修复小程序订阅消息接口没有resp的问题 + +SDK1.8.2 +1. SDK增加开发票授权 WXInvoiceAuthInsert +2. SDK增加非税接口 WXNontaxPay +3. SDK增加医保接口 WXPayInsurance +4. 更换MTA库 + +SDK1.8.1 +1. SDK打开小程序支持指定版本(体验,开发,正式版) +2. SDK分享小程序支持指定版本(体验,开发,正式版) +3. SDK支持输出log日志 + +SDK1.8.0 +1. SDK支持打开小程序 +2. SDK分享小程序支持shareTicket + +SDK1.7.9 +1. SDK订阅一次性消息 + +SDK1.7.8 +1 SDK分享小程序支持大图 + +SDK1.7.7 +1 增加SDK分享小程序 +2 增加选择发票接口 + +SDK1.7.6 +1. 提高稳定性 +1 修复mta崩溃 +2 新增接口支持开发者关闭mta数据统计上报 + +SDK1.7.5 +1. 提高稳定性 +2. 加快registerApp接口启动速度 + +SDK1.7.4 +1. 更新支持iOS启用 ATS(App Transport Security) +2. 需要在工程中链接CFNetwork.framework +3. 在工程配置中的”Other Linker Flags”中加入”-Objc -all_load” + +SDK1.7.3 +1. 增强稳定性,适配iOS10 +2. 修复小于32K的jpg格式缩略图设置失败的问题 + +SDK1.7.2 +1. 修复因CTTeleponyNetworkInfo引起的崩溃问题 + +SDK1.7.1 +1. 支持兼容ipv6(提升稳定性) +2. xCode Version 7.3.1 (7D1014) 编译 + +SDK1.7 +1. 支持兼容ipv6 +2. 修复若干问题增强稳定性 + +SDK1.6.3 +1. xCode7.2 构建的sdk包。 +2. 请使用xCode7.2进行编译。 +3. 需要在Build Phases中Link Security.framework +4. 修复若干小问题。 + +SDK1.6.2 +1、xCode7.1 构建的sdk包 +2、请使用xCode7.1进行编译 + +SDK1.6.1 +1、修复armv7s下,bitcode可能编译不过 +2、解决warning + +SDK1.6 +1、iOS 9系统策略更新,限制了http协议的访问,此外应用需要在“Info.plist”中将要使用的URL Schemes列为白名单,才可正常检查其他应用是否安装。 +受此影响,当你的应用在iOS 9中需要使用微信SDK的相关能力(分享、收藏、支付、登录等)时,需要在“Info.plist”里增加如下代码: +LSApplicationQueriesSchemes + +weixin + +NSAppTransportSecurity + +NSAllowsArbitraryLoads + + +2、开发者需要在工程中链接上 CoreTelephony.framework +3、解决bitcode编译不过问题 + +SDK1.5 +1、废弃safeSendReq:接口,使用sendReq:即可。 +2、新增+(BOOL) sendAuthReq:(SendAuthReq*) req viewController : (UIViewController*) viewController delegate:(id) delegate; +支持未安装微信情况下Auth,具体见WXApi.h接口描述 +3、微信开放平台新增了微信模块用户统计功能,便于开发者统计微信功能模块的用户使用和活跃情况。开发者需要在工程中链接上:SystemConfiguration.framework,libz.dylib,libsqlite3.0.dylib。 diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Headers/WXApi.h b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Headers/WXApi.h new file mode 100644 index 0000000..879c505 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Headers/WXApi.h @@ -0,0 +1,231 @@ +// +// WXApi.h +// 所有Api接口 +// +// Created by Wechat on 12-2-28. +// Copyright (c) 2012年 Tencent. All rights reserved. +// + +#import +#import "WXApiObject.h" + +NS_ASSUME_NONNULL_BEGIN + + +typedef BOOL(^WXGrantReadPasteBoardPermissionCompletion)(void); + + +#pragma mark - WXApiDelegate +/*! @brief 接收并处理来自微信终端程序的事件消息 + * + * 接收并处理来自微信终端程序的事件消息,期间微信界面会切换到第三方应用程序。 + * WXApiDelegate 会在handleOpenURL:delegate:中使用并触发。 + */ +@protocol WXApiDelegate +@optional + +/*! @brief 收到一个来自微信的请求,第三方应用程序处理完后调用sendResp向微信发送结果 + * + * 收到一个来自微信的请求,异步处理完成后必须调用sendResp发送处理结果给微信。 + * 可能收到的请求有GetMessageFromWXReq、ShowMessageFromWXReq等。 + * @param req 具体请求内容,是自动释放的 + */ +- (void)onReq:(BaseReq*)req; + + + +/*! @brief 发送一个sendReq后,收到微信的回应 + * + * 收到一个来自微信的处理结果。调用一次sendReq后会收到onResp。 + * 可能收到的处理结果有SendMessageToWXResp、SendAuthResp等。 + * @param resp具体的回应内容,是自动释放的 + */ +- (void)onResp:(BaseResp*)resp; + +/* ! @brief 用于在iOS16以及以上系统上,控制OpenSDK是否读取剪切板中微信传递的数据以及读取的时机 + * 在iOS16以及以上系统,在SDK需要读取剪切板中微信写入的数据时,会回调该方法。没有实现默认会直接读取微信通过剪切板传递过来的数据 + * 注意: + * 1. 只在iOS16以及以上的系统版本上回调; + * 2. 不实现时,OpenSDK会直接调用读取剪切板接口,读取微信传递过来的数据; + * 3. 若实现该方法:开发者需要通过调用completion(), 支持异步,通知SDK允许读取剪切板中微信传递的数据, + * 不调用completion()则代表不授权OpenSDK读取剪切板,会导致收不到onReq:, onResp:回调,无法后续业务流程。请谨慎使用 + * 4. 不要长时间持有completion不释放,可能会导致内存泄漏。 + */ +- (void)onNeedGrantReadPasteBoardPermissionWithURL:(nonnull NSURL *)openURL completion:(nonnull WXGrantReadPasteBoardPermissionCompletion)completion; + +@end + +#pragma mark - WXApiLogDelegate + +@protocol WXApiLogDelegate + +- (void)onLog:(NSString*)log logLevel:(WXLogLevel)level; + +@end + + +#pragma mark - WXApi + +/*! @brief 微信Api接口函数类 + * + * 该类封装了微信终端SDK的所有接口 + */ +@interface WXApi : NSObject + +/*! @brief WXApi的成员函数,向微信终端程序注册第三方应用。 + * + * 需要在每次启动第三方应用程序时调用。 + * @attention 请保证在主线程中调用此函数 + * @param appid 微信开发者ID + * @param universalLink 微信开发者Universal Link + * @return 成功返回YES,失败返回NO。 + */ ++ (BOOL)registerApp:(NSString *)appid universalLink:(NSString *)universalLink; + + +/*! @brief 处理旧版微信通过URL启动App时传递的数据 + * + * 需要在 application:openURL:sourceApplication:annotation:或者application:handleOpenURL中调用。 + * @param url 微信启动第三方应用时传递过来的URL + * @param delegate WXApiDelegate对象,用来接收微信触发的消息。 + * @return 成功返回YES,失败返回NO。 + */ ++ (BOOL)handleOpenURL:(NSURL *)url delegate:(nullable id)delegate; + + +/*! @brief 处理微信通过Universal Link启动App时传递的数据 + * + * 需要在 application:continueUserActivity:restorationHandler:中调用。 + * @param userActivity 微信启动第三方应用时系统API传递过来的userActivity + * @param delegate WXApiDelegate对象,用来接收微信触发的消息。 + * @return 成功返回YES,失败返回NO。 + */ ++ (BOOL)handleOpenUniversalLink:(NSUserActivity *)userActivity delegate:(nullable id)delegate; + + +/*! @brief 检查微信是否已被用户安装 + * + * @return 微信已安装返回YES,未安装返回NO。 + */ ++ (BOOL)isWXAppInstalled; + + + +/*! @brief 判断当前微信的版本是否支持OpenApi + * + * @return 支持返回YES,不支持返回NO。 + */ ++ (BOOL)isWXAppSupportApi; + + +/*! @brief 判断当前微信的版本是否支持分享微信状态功能 + * + * @attention 需在工程LSApplicationQueriesSchemes配置中添加weixinStateAPI + * @return 支持返回YES,不支持返回NO。 + */ ++ (BOOL)isWXAppSupportStateAPI; + + +#ifndef BUILD_WITHOUT_PAY +/*! @brief 判断当前微信的版本是否支持二维码拉起微信支付 + * + * @attention 需在工程LSApplicationQueriesSchemes配置中添加weixinQRCodePayAPI + * @return 支持返回YES,不支持返回NO。 + */ ++ (BOOL)isWXAppSupportQRCodePayAPI; + +#endif + + +/*! @brief 获取微信的itunes安装地址 + * + * @return 微信的安装地址字符串。 + */ ++ (NSString *)getWXAppInstallUrl; + + + +/*! @brief 获取当前微信SDK的版本号 + * + * @return 返回当前微信SDK的版本号 + */ ++ (NSString *)getApiVersion; + + + +/*! @brief 打开微信 + * + * @return 成功返回YES,失败返回NO。 + */ ++ (BOOL)openWXApp; + + + +/*! @brief 发送请求到微信,等待微信返回onResp + * + * 函数调用后,会切换到微信的界面。第三方应用程序等待微信返回onResp。微信在异步处理完成后一定会调用onResp。支持以下类型 + * SendAuthReq、SendMessageToWXReq、PayReq等。 + * @param req 具体的发送请求。 + * @param completion 调用结果回调block + */ ++ (void)sendReq:(BaseReq *)req completion:(void (^ __nullable)(BOOL success))completion; + +/*! @brief 收到微信onReq的请求,发送对应的应答给微信,并切换到微信界面 + * + * 函数调用后,会切换到微信的界面。第三方应用程序收到微信onReq的请求,异步处理该请求,完成后必须调用该函数。可能发送的相应有 + * GetMessageFromWXResp、ShowMessageFromWXResp等。 + * @param resp 具体的应答内容 + * @param completion 调用结果回调block + */ ++ (void)sendResp:(BaseResp*)resp completion:(void (^ __nullable)(BOOL success))completion; + + +/*! @brief 发送Auth请求到微信,支持用户没安装微信,等待微信返回onResp + * + * 函数调用后,会切换到微信的界面。第三方应用程序等待微信返回onResp。微信在异步处理完成后一定会调用onResp。支持SendAuthReq类型。 + * @param req 具体的发送请求。 + * @param viewController 当前界面对象。 + * @param delegate WXApiDelegate对象,用来接收微信触发的消息。 + * @param completion 调用结果回调block + */ ++ (void)sendAuthReq:(SendAuthReq *)req viewController:(UIViewController*)viewController delegate:(nullable id)delegate completion:(void (^ __nullable)(BOOL success))completion; + + +/*! @brief 测试函数,用于排查当前App通过Universal Link方式分享到微信的流程 + 注意1: 调用自检函数之前必须要先调用registerApp:universalLink接口, 并确认调用成功 + 注意2: 自检过程中会有Log产生,可以先调用startLogByLevel函数,根据Log排查问题 + 注意3: 会多次回调block + 注意4: 仅用于新接入SDK时调试使用,请勿在正式环境的调用 + * + * 当completion回调的step为WXULCheckStepFinal时,表示检测通过,Universal Link接入成功 + * @param completion 回调Block + */ ++ (void)checkUniversalLinkReady:(nonnull WXCheckULCompletion)completion; + + +/*! @brief WXApi的成员函数,接受微信的log信息。byBlock + 注意1:SDK会强引用这个block,注意不要导致内存泄漏,注意不要导致内存泄漏 + 注意2:调用过一次startLog by block之后,如果再调用一次任意方式的startLoad,会释放上一次logBlock,不再回调上一个logBlock + * + * @param level 打印log的级别 + * @param logBlock 打印log的回调block + */ + ++ (void)startLogByLevel:(WXLogLevel)level logBlock:(WXLogBolock)logBlock; + +/*! @brief WXApi的成员函数,接受微信的log信息。byDelegate + 注意1:sdk会弱引用这个delegate,这里可加任意对象为代理,不需要与WXApiDelegate同一个对象 + 注意2:调用过一次startLog by delegate之后,再调用一次任意方式的startLoad,不会再回调上一个logDelegate对象 + * @param level 打印log的级别 + * @param logDelegate 打印log的回调代理, + */ ++ (void)startLogByLevel:(WXLogLevel)level logDelegate:(id)logDelegate; + +/*! @brief 停止打印log,会清理block或者delegate为空,释放block + * @param + */ ++ (void)stopLog; + +@end + +NS_ASSUME_NONNULL_END diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Headers/WXApiObject.h b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Headers/WXApiObject.h new file mode 100644 index 0000000..85b6457 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Headers/WXApiObject.h @@ -0,0 +1,1410 @@ +// +// MMApiObject.h +// Api对象,包含所有接口和对象数据定义 +// +// Created by Wechat on 12-2-28. +// Copyright (c) 2012年 Tencent. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief 错误码 + * + */ +enum WXErrCode { + WXSuccess = 0, /**< 成功 */ + WXErrCodeCommon = -1, /**< 普通错误类型 */ + WXErrCodeUserCancel = -2, /**< 用户点击取消并返回 */ + WXErrCodeSentFail = -3, /**< 发送失败 */ + WXErrCodeAuthDeny = -4, /**< 授权失败 */ + WXErrCodeUnsupport = -5, /**< 微信不支持 */ +}; + + + +/*! @brief 请求发送场景 + * + */ +enum WXScene { + WXSceneSession = 0, /**< 聊天界面 */ + WXSceneTimeline = 1, /**< 朋友圈 */ + WXSceneFavorite = 2, /**< 收藏 */ + WXSceneSpecifiedSession = 3, /**< 指定联系人 */ + WXSceneState = 4, /**< 状态 */ +}; + + +enum WXAPISupport { + WXAPISupportSession = 0, +}; + + + +/*! @brief 跳转profile类型 + * + */ +enum WXBizProfileType { + WXBizProfileType_Normal = 0, //**< 普通公众号 */ + WXBizProfileType_Device = 1, //**< 硬件公众号 */ +}; + +/*! @brief 分享小程序类型 + * + */ +typedef NS_ENUM(NSUInteger, WXMiniProgramType) { + WXMiniProgramTypeRelease = 0, //**< 正式版 */ + WXMiniProgramTypeTest = 1, //**< 开发版 */ + WXMiniProgramTypePreview = 2, //**< 体验版 */ +}; + +/*! @brief 跳转mp网页类型 + * + */ +enum WXMPWebviewType { + WXMPWebviewType_Ad = 0, /**< 广告网页 **/ +}; + +/*! @brief log的级别 + * + */ +typedef NS_ENUM(NSInteger,WXLogLevel) { + WXLogLevelNormal = 0, // 打印日常的日志 + WXLogLevelDetail = 1, // 打印详细的日志 +}; + + +/*! @brief 打印回调的block + * + */ +typedef void(^WXLogBolock)(NSString *log); + +/*! @brief 微信Universal Link检查函数 (WXApi#checkUniversalLinkReady:),检查步骤枚举值 + * + * WXULCheckStepParams 参数检测 + * WXULCheckStepSystemVersion 当前系统版本检测 + * WXULCheckStepWechatVersion 微信客户端版本检测 + * WXULCheckStepSDKInnerOperation 微信SDK内部操作检测 + * WXULCheckStepLaunchWechat App拉起微信检测 + * WXULCheckStepBackToCurrentApp 由微信返回当前App检测 + * WXULCheckStepFinal 最终结果 + */ +typedef NS_ENUM(NSInteger, WXULCheckStep) +{ + WXULCheckStepParams, + WXULCheckStepSystemVersion, + WXULCheckStepWechatVersion, + WXULCheckStepSDKInnerOperation, + WXULCheckStepLaunchWechat, + WXULCheckStepBackToCurrentApp, + WXULCheckStepFinal, +}; + + +#pragma mark - WXCheckULStepResult + +/*! @brief 该类为微信Universal Link检测函数结果类 +* +*/ +@interface WXCheckULStepResult : NSObject + +/** 是否成功 */ +@property(nonatomic, assign) BOOL success; +/** 当前错误信息 */ +@property(nonatomic, strong) NSString* errorInfo; +/** 修正建议 */ +@property(nonatomic, strong) NSString* suggestion; + +- (instancetype)initWithCheckResult:(BOOL)success errorInfo:(nullable NSString*)errorInfo suggestion:(nullable NSString*)suggestion; + +@end + + +/*! @brief 微信Universal Link检查函数 (WXApi#checkUniversalLinkReady:),回调Block + * + * @param step 当前检测步骤 + * @param result 检测结果 + */ +typedef void(^WXCheckULCompletion)(WXULCheckStep step, WXCheckULStepResult* result); + + +#pragma mark - BaseReq +/*! @brief 该类为微信终端SDK所有请求类的基类 + * + */ +@interface BaseReq : NSObject + +/** 请求类型 */ +@property (nonatomic, assign) int type; +/** 由用户微信号和AppID组成的唯一标识,需要校验微信用户是否换号登录时填写*/ +@property (nonatomic, copy) NSString *openID; + +@end + + + +#pragma mark - BaseResp +/*! @brief 该类为微信终端SDK所有响应类的基类 + * + */ +@interface BaseResp : NSObject +/** 错误码 */ +@property (nonatomic, assign) int errCode; +/** 错误提示字符串 */ +@property (nonatomic, copy) NSString *errStr; +/** 响应类型 */ +@property (nonatomic, assign) int type; + +@end + + + +#pragma mark - WXMediaMessage +@class WXMediaMessage; + +#ifndef BUILD_WITHOUT_PAY + +#pragma mark - PayReq +/*! @brief 第三方向微信终端发起支付的消息结构体 + * + * 第三方向微信终端发起支付的消息结构体,微信终端处理后会向第三方返回处理结果 + * @see PayResp + */ +@interface PayReq : BaseReq + +/** 商家向财付通申请的商家id */ +@property (nonatomic, copy) NSString *partnerId; +/** 预支付订单 */ +@property (nonatomic, copy) NSString *prepayId; +/** 随机串,防重发 */ +@property (nonatomic, copy) NSString *nonceStr; +/** 时间戳,防重发 */ +@property (nonatomic, assign) UInt32 timeStamp; +/** 商家根据财付通文档填写的数据和签名 */ +@property (nonatomic, copy) NSString *package; +/** 商家根据微信开放平台文档对数据做的签名 */ +@property (nonatomic, copy) NSString *sign; + +@end + + +#pragma mark - PayResp +/*! @brief 微信终端返回给第三方的关于支付结果的结构体 + * + * 微信终端返回给第三方的关于支付结果的结构体 + */ +@interface PayResp : BaseResp + +/** 财付通返回给商家的信息 */ +@property (nonatomic, copy) NSString *returnKey; + +@end + +#pragma mark - WXOfflinePay +/*! @brief 第三方向微信终端发起离线支付 + * + * 第三方向微信终端发起离线支付的消息结构体 + */ +@interface WXOfflinePayReq : BaseReq + +@end + +/*! @brief 第三方向微信终端发起离线支付返回 + * + * 第三方向微信终端发起离线支付返回的消息结构体 + */ +@interface WXOfflinePayResp : BaseResp + +@end + + +#pragma mark - WXNontaxPayReq +@interface WXNontaxPayReq:BaseReq + +@property (nonatomic, copy) NSString *urlString; + +@end + +#pragma mark - WXNontaxPayResp +@interface WXNontaxPayResp : BaseResp + +@property (nonatomic, copy) NSString *wxOrderId; + +@end + +#pragma mark - WXPayInsuranceReq +@interface WXPayInsuranceReq : BaseReq + +@property (nonatomic, copy) NSString *urlString; + +@end + +#pragma mark - WXPayInsuranceResp +@interface WXPayInsuranceResp : BaseResp + +@property (nonatomic, copy) NSString *wxOrderId; + +@end + + +#pragma mark - WXQRCodePayReq +@interface WXQRCodePayReq : BaseReq +/** 码内容 + * @note 必填,码长度必须大于0且小于10K + */ +@property (nonatomic, copy) NSString *codeContent; +/** 额外信息 + * @note 长度必须大于0且小于10K + */ +@property (nonatomic, copy) NSString *extraMsg; + +@end + +@interface WXQRCodePayResp : BaseResp +@end + +#endif + + +#pragma mark - SendAuthReq +/*! @brief 第三方程序向微信终端请求认证的消息结构 + * + * 第三方程序要向微信申请认证,并请求某些权限,需要调用WXApi的sendReq成员函数, + * 向微信终端发送一个SendAuthReq消息结构。微信终端处理完后会向第三方程序发送一个处理结果。 + * @see SendAuthResp + */ +@interface SendAuthReq : BaseReq +/** 第三方程序要向微信申请认证,并请求某些权限,需要调用WXApi的sendReq成员函数,向微信终端发送一个SendAuthReq消息结构。微信终端处理完后会向第三方程序发送一个处理结果。 + * @see SendAuthResp + * @note scope字符串长度不能超过1K + */ +@property (nonatomic, copy) NSString *scope; +/** 第三方程序本身用来标识其请求的唯一性,最后跳转回第三方程序时,由微信终端回传。 + * @note state字符串长度不能超过1K + */ +@property (nonatomic, copy) NSString *state; + +@property (nonatomic, assign) BOOL isOption1; + +/** 是否关闭自动授权 + * @note YES为关闭自动授权,每次登陆都需要用户手动授权;NO为允许自动授权 + */ +@property (nonatomic, assign) BOOL nonautomatic; + +@property (nonatomic, copy) NSString *extData; + +@end + +#pragma mark - SendAuthResp +/*! @brief 微信处理完第三方程序的认证和权限申请后向第三方程序回送的处理结果。 + * + * 第三方程序要向微信申请认证,并请求某些权限,需要调用WXApi的sendReq成员函数,向微信终端发送一个SendAuthReq消息结构。 + * 微信终端处理完后会向第三方程序发送一个SendAuthResp。 + * @see onResp + */ +@interface SendAuthResp : BaseResp +@property (nonatomic, copy, nullable) NSString *code; +/** 第三方程序发送时用来标识其请求的唯一性的标志,由第三方程序调用sendReq时传入,由微信终端回传 + * @note state字符串长度不能超过1K + * @note 在复杂度较高的应用程序中,可能会出现其他模块请求的响应对象被错误地回调到当前模块。 + * 为了避免影响其他模块,建议当前模块的开发者根据SendAuthResp的内容,采用白名单的方式进行处理。 + * 例如,SendAuthResp.state符合预期时,才对其进行处理。 + */ +@property (nonatomic, copy, nullable) NSString *state; +@property (nonatomic, copy, nullable) NSString *lang; +@property (nonatomic, copy, nullable) NSString *country; +@end + +#pragma mark - WXStateJumpInfo +/*! @brief 状态发表时的小尾巴跳转信息 + */ +@interface WXStateJumpInfo : NSObject + +@end + +#pragma mark - WXStateJumpUrlInfo +/*! @brief 状态小尾巴跳转指定url的信息 + */ +@interface WXStateJumpUrlInfo : WXStateJumpInfo +/** 跳转到指定的url + * @note 必填,url长度必须大于0且小于10K + */ +@property (nonatomic, copy) NSString *url; + +@end + +#pragma mark - WXStateJumpWXMiniProgramInfo +/*! @brief 状态小尾巴跳转指定小程序的信息 + */ +@interface WXStateJumpMiniProgramInfo : WXStateJumpInfo +/** 小程序username + * @note 必填 + */ +@property (nonatomic, copy) NSString *username; + +/** 小程序页面的路径 + * @attention 不填默认拉起小程序首页 + */ +@property (nonatomic, copy, nullable) NSString *path; + +/** 分享小程序的版本 + * @attention (正式,开发,体验) + */ +@property (nonatomic, assign) WXMiniProgramType miniProgramType; + +@end + + + +#pragma mark - WXStateJumpWXMiniProgramInfo +/*! @brief 状态小尾巴跳转指定视频号主页信息 + */ +@interface WXStateJumpChannelProfileInfo : WXStateJumpInfo +/** 视频号username + * @note 必填,username长度必须大于0且小于1K + */ +@property (nonatomic, copy) NSString *username; + + +@end + +#pragma mark - WXStateSceneDataObject +/*! @brief 场景类型额外参数基类 + */ +@interface WXSceneDataObject : NSObject + +@end + +#pragma mark - WXStateSceneDataObject +/*! @brief 状态场景类型 + * 用户填写WXStateSceneDataObject参数后,可以跳转到微信状态发表页 + */ +@interface WXStateSceneDataObject : WXSceneDataObject + +/** 状态标志的ID + * @note 选填,文本长度必须小于10K + */ +@property (nonatomic, copy) NSString *stateId; +/** 状态发表时附带的文本描述 + * @note 选填,文本长度必须小于10K + */ +@property (nonatomic, copy) NSString *stateTitle; +/** 后台校验token + * @note 选填,文本长度必须小于10K + */ +@property (nonatomic, copy) NSString *token; +/** 小尾巴跳转所需的信息 + * @note 必填,目前仅支持url跳转 + */ +@property (nonatomic, strong) WXStateJumpInfo *stateJumpDataInfo; + +@end + +#pragma mark - SendMessageToWXReq +/*! @brief 第三方程序发送消息至微信终端程序的消息结构体 + * + * 第三方程序向微信发送信息需要传入SendMessageToWXReq结构体,信息类型包括文本消息和多媒体消息, + * 分别对应于text和message成员。调用该方法后,微信处理完信息会向第三方程序发送一个处理结果。 + * @see SendMessageToWXResp + */ +@interface SendMessageToWXReq : BaseReq +/** 发送消息的文本内容 + * @note 文本长度必须大于0且小于10K + */ +@property (nonatomic, copy) NSString *text; +/** 发送消息的多媒体内容 + * @see WXMediaMessage + */ +@property (nonatomic, strong) WXMediaMessage *message; +/** 发送消息的类型,包括文本消息和多媒体消息两种,两者只能选择其一,不能同时发送文本和多媒体消息 */ +@property (nonatomic, assign) BOOL bText; +/** 发送的目标场景,可以选择发送到会话(WXSceneSession)或者朋友圈(WXSceneTimeline)。 默认发送到会话。 + * @see WXScene + */ +@property (nonatomic, assign) int scene; +/** 指定发送消息的人 + * @note WXSceneSpecifiedSession时有效 + */ +@property (nonatomic, copy, nullable) NSString *toUserOpenId; +/** 目标场景附带信息 + * @note 目前只针对状态场景 + */ +@property (nonatomic, strong) WXSceneDataObject *sceneDataObject; + +@end + +#pragma mark - SendMessageToWXResp +/*! @brief 微信终端向第三方程序返回的SendMessageToWXReq处理结果。 + * + * 第三方程序向微信终端发送SendMessageToWXReq后,微信发送回来的处理结果,该结果用SendMessageToWXResp表示。 + */ +@interface SendMessageToWXResp : BaseResp +@property(nonatomic, copy) NSString *lang; +@property(nonatomic, copy) NSString *country; +@end + +#pragma mark - GetMessageFromWXReq +/*! @brief 微信终端向第三方程序请求提供内容的消息结构体。 + * + * 微信终端向第三方程序请求提供内容,微信终端会向第三方程序发送GetMessageFromWXReq消息结构体, + * 需要第三方程序调用sendResp返回一个GetMessageFromWXResp消息结构体。 + */ +@interface GetMessageFromWXReq : BaseReq +@property (nonatomic, strong) NSString *lang; +@property (nonatomic, strong) NSString *country; +@end + + + +#pragma mark - GetMessageFromWXResp +/*! @brief 微信终端向第三方程序请求提供内容,第三方程序向微信终端返回的消息结构体。 + * + * 微信终端向第三方程序请求提供内容,第三方程序调用sendResp向微信终端返回一个GetMessageFromWXResp消息结构体。 + */ +@interface GetMessageFromWXResp : BaseResp +/** 向微信终端提供的文本内容 + @note 文本长度必须大于0且小于10K + */ +@property (nonatomic, strong) NSString *text; +/** 向微信终端提供的多媒体内容。 + * @see WXMediaMessage + */ +@property (nonatomic, strong) WXMediaMessage *message; +/** 向微信终端提供内容的消息类型,包括文本消息和多媒体消息两种,两者只能选择其一,不能同时发送文本和多媒体消息 */ +@property (nonatomic, assign) BOOL bText; +@end + + + +#pragma mark - ShowMessageFromWXReq +/*! @brief 微信通知第三方程序,要求第三方程序显示的消息结构体。 + * + * 微信需要通知第三方程序显示或处理某些内容时,会向第三方程序发送ShowMessageFromWXReq消息结构体。 + * 第三方程序处理完内容后调用sendResp向微信终端发送ShowMessageFromWXResp。 + */ +@interface ShowMessageFromWXReq : BaseReq +/** 微信终端向第三方程序发送的要求第三方程序处理的多媒体内容 + * @see WXMediaMessage + */ +@property (nonatomic, strong) WXMediaMessage *message; +@property (nonatomic, copy) NSString *lang; +@property (nonatomic, copy) NSString *country; +@end + + + +#pragma mark - ShowMessageFromWXResp +/*! @brief 微信通知第三方程序,要求第三方程序显示或处理某些消息,第三方程序处理完后向微信终端发送的处理结果。 + * + * 微信需要通知第三方程序显示或处理某些内容时,会向第三方程序发送ShowMessageFromWXReq消息结构体。 + * 第三方程序处理完内容后调用sendResp向微信终端发送ShowMessageFromWXResp。 + */ +@interface ShowMessageFromWXResp : BaseResp +@end + + +#pragma mark - LaunchFromWXReq +/*! @brief 微信终端打开第三方程序携带的消息结构体 + * + * 微信向第三方发送的结构体,第三方不需要返回 + */ +@interface LaunchFromWXReq : BaseReq +@property (nonatomic, strong) WXMediaMessage *message; +@property (nonatomic, copy) NSString *lang; +@property (nonatomic, copy) NSString *country; +@end + + +#pragma mark - OpenWebviewReq +/* ! @brief 第三方通知微信启动内部浏览器,打开指定网页 + * + * 第三方通知微信启动内部浏览器,打开指定Url对应的网页 + */ +@interface OpenWebviewReq : BaseReq +/** 需要打开的网页对应的Url + * @attention 长度不能超过1024 + */ +@property(nonatomic, copy) NSString *url; + +@end + +#pragma mark - OpenWebviewResp +/*! @brief 微信终端向第三方程序返回的OpenWebviewReq处理结果 + * + * 第三方程序向微信终端发送OpenWebviewReq后,微信发送回来的处理结果,该结果用OpenWebviewResp表示 + */ +@interface OpenWebviewResp : BaseResp + +@end + + + +#pragma mark - WXOpenBusinessWebViewReq +/*! @brief 第三方通知微信启动内部浏览器,打开指定业务的网页 + * + * + */ +@interface WXOpenBusinessWebViewReq : BaseReq + +/** 网页业务类型 + * @attention + */ +@property (nonatomic, assign) UInt32 businessType; + +/** 网页业务参数 + * @attention + */ +@property (nonatomic, strong, nullable) NSDictionary *queryInfoDic; + +@end + +#pragma mark - WXOpenBusinessWebViewResp +/*! @brief 微信终端向第三方程序返回的WXOpenBusinessWebViewResp处理结果。 + * + * 第三方程序向微信终端发送WXOpenBusinessWebViewReq后,微信发送回来的处理结果,该结果用WXOpenBusinessWebViewResp表示。 + */ +@interface WXOpenBusinessWebViewResp : BaseResp +/** 第三方程序自定义简单数据,微信终端会回传给第三方程序处理 + * @attention 长度不能超过2k + */ +@property (nonatomic, copy) NSString *result; + +/** 网页业务类型 + * @attention + */ +@property (nonatomic, assign) UInt32 businessType; + +@end + + +#pragma mark - OpenRankListReq +/* ! @brief 第三方通知微信,打开硬件排行榜 + * + * 第三方通知微信,打开硬件排行榜 + */ +@interface OpenRankListReq : BaseReq + +@end + +#pragma mark - OpenRanklistResp +/*! @brief 微信终端向第三方程序返回的OpenRankListReq处理结果。 + * + * 第三方程序向微信终端发送OpenRankListReq后,微信发送回来的处理结果,该结果用OpenRankListResp表示。 + */ +@interface OpenRankListResp : BaseResp + +@end + + +#pragma mark - WXCardItem + +@interface WXCardItem : NSObject +/** 卡id + * @attention 长度不能超过1024字节 + */ +@property (nonatomic, copy) NSString *cardId; +/** ext信息 + * @attention 长度不能超过2024字节 + */ +@property (nonatomic, copy, nullable) NSString *extMsg; +/** + * @attention 卡的状态,req不需要填。resp:0为未添加,1为已添加。 + */ +@property (nonatomic, assign) UInt32 cardState; +/** + * @attention req不需要填,chooseCard返回的。 + */ +@property (nonatomic, copy) NSString *encryptCode; +/** + * @attention req不需要填,chooseCard返回的。 + */ +@property (nonatomic, copy) NSString *appID; +@end; + +#pragma mark - WXInvoiceItem + +@interface WXInvoiceItem : NSObject +/** 卡id + * @attention 长度不能超过1024字节 + */ +@property (nonatomic, copy) NSString *cardId; +/** ext信息 + * @attention 长度不能超过2024字节 + */ +@property (nonatomic, copy, nullable) NSString *extMsg; +/** + * @attention 卡的状态,req不需要填。resp:0为未添加,1为已添加。 + */ +@property (nonatomic, assign) UInt32 cardState; +/** + * @attention req不需要填,chooseCard返回的。 + */ +@property (nonatomic, copy) NSString *encryptCode; +/** + * @attention req不需要填,chooseCard返回的。 + */ +@property (nonatomic, copy) NSString *appID; + +@end + +#pragma mark - AddCardToWXCardPackageReq +/* ! @brief 请求添加卡券至微信卡包 + * + */ + +@interface AddCardToWXCardPackageReq : BaseReq +/** 卡列表 + * @attention 个数不能超过40个 类型WXCardItem + */ +@property (nonatomic, strong) NSArray *cardAry; + +@end + + +#pragma mark - AddCardToWXCardPackageResp +/** ! @brief 微信返回第三方添加卡券结果 + * + */ + +@interface AddCardToWXCardPackageResp : BaseResp +/** 卡列表 + * @attention 个数不能超过40个 类型WXCardItem + */ +@property (nonatomic, strong) NSArray *cardAry; +@end + +#pragma mark - WXChooseCardReq +/* ! @brief 请求从微信选取卡券 + * + */ + +@interface WXChooseCardReq : BaseReq +@property (nonatomic, copy) NSString *appID; +@property (nonatomic, assign) UInt32 shopID; +@property (nonatomic, assign) UInt32 canMultiSelect; +@property (nonatomic, copy) NSString *cardType; +@property (nonatomic, copy) NSString *cardTpID; +@property (nonatomic, copy) NSString *signType; +@property (nonatomic, copy) NSString *cardSign; +@property (nonatomic, assign) UInt32 timeStamp; +@property (nonatomic, copy) NSString *nonceStr; +@end + + +#pragma mark - WXChooseCardResp +/** ! @brief 微信返回第三方请求选择卡券结果 + * + */ + +@interface WXChooseCardResp : BaseResp +@property (nonatomic, strong ) NSArray* cardAry; +@end + + +#pragma mark - WXChooseInvoiceReq +/* ! @brief 请求从微信选取发票 + * + */ +@interface WXChooseInvoiceReq : BaseReq +@property (nonatomic, copy) NSString *appID; +@property (nonatomic, assign) UInt32 shopID; +@property (nonatomic, copy) NSString *signType; +@property (nonatomic, copy) NSString *cardSign; +@property (nonatomic, assign) UInt32 timeStamp; +@property (nonatomic, copy) NSString *nonceStr; +@end + +#pragma mark - WXChooseInvoiceResp +/** ! @brief 微信返回第三方请求选择发票结果 + * + */ +@interface WXChooseInvoiceResp : BaseResp +@property (nonatomic, strong) NSArray* cardAry; +@end + +#pragma mark - WXSubscriptionReq +@interface WXSubscribeMsgReq : BaseReq +@property (nonatomic, assign) UInt32 scene; +@property (nonatomic, copy) NSString *templateId; +@property (nonatomic, copy, nullable) NSString *reserved; +@end + +#pragma mark - WXSubscriptionReq +@interface WXSubscribeMsgResp : BaseResp + +@property (nonatomic, copy) NSString *templateId; +@property (nonatomic, assign) UInt32 scene; +@property (nonatomic, copy) NSString *action; +@property (nonatomic, copy) NSString *reserved; +@property (nonatomic, copy, nullable) NSString *openId; + +@end + +#pragma mark - WXSubscribeMiniProgramMsg +/** ! @brief 第三方请求订阅小程序消息 + * + */ +@interface WXSubscribeMiniProgramMsgReq : BaseReq +@property (nonatomic, copy) NSString *miniProgramAppid; +@end + +#pragma mark - WXSubscriptionReq +@interface WXSubscribeMiniProgramMsgResp : BaseResp + +@property(nonatomic, copy) NSString *openId; // 小程序openid +@property(nonatomic, copy) NSString *unionId; // unionId +@property(nonatomic, copy) NSString *nickName; // 用户昵称 + +@end + +#pragma mark - WXinvoiceAuthInsertReq +@interface WXInvoiceAuthInsertReq : BaseReq + +@property (nonatomic, copy) NSString *urlString; + +@end + +#pragma mark - WXinvoiceAuthInsertResp + +@interface WXInvoiceAuthInsertResp : BaseResp + +@property (nonatomic, copy) NSString *wxOrderId; + +@end + +#pragma mark - WXMediaMessage + +/*! @brief 多媒体消息结构体 + * + * 用于微信终端和第三方程序之间传递消息的多媒体消息内容 + */ +@interface WXMediaMessage : NSObject + ++ (WXMediaMessage *)message; + +/** 标题 + * @note 长度不能超过512字节 + */ +@property (nonatomic, copy) NSString *title; +/** 描述内容 + * @note 长度不能超过1K + */ +@property (nonatomic, copy) NSString *description; +/** 缩略图数据 + * @note 大小不能超过64K + */ +@property (nonatomic, strong, nullable) NSData *thumbData; +/** + * @note 长度不能超过64字节 + */ +@property (nonatomic, copy, nullable) NSString *mediaTagName; +/** + * + */ +@property (nonatomic, copy, nullable) NSString *messageExt; +@property (nonatomic, copy, nullable) NSString *messageAction; +/** + * 多媒体数据对象,可以为WXImageObject,WXMusicObject,WXVideoObject,WXWebpageObject等。 + */ +@property (nonatomic, strong) id mediaObject; + +/** 缩略图的hash值 + * @note 使用sha256得到,用于计算签名 + */ +@property (nonatomic, copy, nullable) NSString *thumbDataHash; + +/** 消息签名 + * @note 用于校验消息体是否被篡改过 + */ +@property (nonatomic, copy, nullable) NSString *msgSignature; + + +/*! @brief 设置消息缩略图的方法 + * + * @param image 缩略图 + * @note 大小不能超过256K + */ +- (void)setThumbImage:(UIImage *)image; + +@end + + + +#pragma mark - WXImageObject +/*! @brief 多媒体消息中包含的图片数据对象 + * + * 微信终端和第三方程序之间传递消息中包含的图片数据对象。 + * @note imageData成员不能为空 + * @see WXMediaMessage + */ +@interface WXImageObject : NSObject +/*! @brief 返回一个WXImageObject对象 + * + * @note 返回的WXImageObject对象是自动释放的 + */ ++ (WXImageObject *)object; + +/** 图片真实数据内容 + * @note 大小不能超过25M + */ +@property (nonatomic, strong) NSData *imageData; + +/** 图片数据的hash值 + * @note 使用sha256得到,用于计算签名 + */ +@property (nonatomic, copy, nullable) NSString *imgDataHash; + +/** 分享的图片消息是否要带小程序入口,若 'entranceMiniProgramUsername' 非空则显示 + * 仅部分小程序类目可用 + * @note 本字段为空则发送普通app图片消息 + */ +@property (nonatomic, copy, nullable) NSString *entranceMiniProgramUsername; + +/** 分享的图片消息显示的小程序入口可以跳转的小程序路径 + * 仅当 'entranceMiniProgramUsername' 非空时生效 + */ +@property (nonatomic, copy, nullable) NSString *entranceMiniProgramPath; + +@end + + +#pragma mark - WXMusicObject +/*! @brief 多媒体消息中包含的音乐数据对象 + * + * 微信终端和第三方程序之间传递消息中包含的音乐数据对象。 + * @note musicUrl和musicLowBandUrl成员不能同时为空。 + * @see WXMediaMessage + */ +@interface WXMusicObject : NSObject +/*! @brief 返回一个WXMusicObject对象 + * + * @note 返回的WXMusicObject对象是自动释放的 + */ ++ (WXMusicObject *)object; + +/** 音乐网页的url地址 + * @note 长度不能超过10K + */ +@property (nonatomic, copy) NSString *musicUrl; +/** 音乐lowband网页的url地址 + * @note 长度不能超过10K + */ +@property (nonatomic, copy) NSString *musicLowBandUrl; +/** 音乐数据url地址 + * @note 长度不能超过10K + */ +@property (nonatomic, copy) NSString *musicDataUrl; + +/**音乐lowband数据url地址 + * @note 长度不能超过10K + */ +@property (nonatomic, copy) NSString *musicLowBandDataUrl; + +/**音乐封面图Url + * @note 长度不能超过10K + */ +@property (nonatomic, copy) NSString *songAlbumUrl; + +/**歌词信息 LRC格式 + * @note 长度不能超过32K + */ +@property (nonatomic, copy, nullable) NSString *songLyric; +@end + + + +#pragma mark - WXMusicVideoObject + +@interface WXMusicVipInfo : NSObject + +/**付费歌曲的id + * @note 长度不能超过32K + */ +@property (nonatomic, copy) NSString *musicId; + +@end + + +@interface WXMusicVideoObject : NSObject + +/*! @brief 返回一个WXMusicVideoObject对象 + * + * @note 返回的WXMusicVideoObject对象是自动释放的 + */ ++ (WXMusicVideoObject *)object; + +/** 音乐网页的url地址 + * @note 长度不能超过10K,不能为空 + */ +@property (nonatomic, copy) NSString *musicUrl; + +/** 音乐数据url地址 + * @note 长度不能超过10K,不能为空 + */ +@property (nonatomic, copy) NSString *musicDataUrl; + +/**歌手名 + * @note 长度不能超过1k,不能为空 + */ +@property (nonatomic, copy) NSString *singerName; + +/** + * @note 音乐时长, 单位毫秒 + */ +@property (nonatomic, assign) UInt32 duration; + +/**歌词信息 LRC格式 + * @note 长度不能超过32K + */ +@property (nonatomic, copy) NSString *songLyric; + +/**高清封面图 + * @note 大小不能超过1M + */ +@property (nonatomic, strong) NSData *hdAlbumThumbData; + +/** 高清封面图数据的hash值 + * @note 使用sha256得到,用于计算签名 + */ +@property (nonatomic, copy, nullable) NSString *hdAlbumThumbFileHash; + +/**音乐专辑名称 + * @note 长度不能超过1k + */ +@property (nonatomic, copy, nullable) NSString *albumName; + +/**音乐流派 + * @note 长度不能超过1k + */ +@property (nonatomic, copy, nullable) NSString *musicGenre; + +/**发行时间 + * @note Unix时间戳,单位为秒 + */ +@property (nonatomic, assign) UInt64 issueDate; + +/**音乐标识符 + * @note 长度不能超过1K,从微信跳回应用时会带上 + */ +@property (nonatomic, copy, nullable) NSString *identification; + +/**运营H5地址 + * @note 选填,建议填写,用户进入歌曲详情页将展示内嵌的运营H5,可展示该歌曲的相关评论、歌曲推荐等内容,不可诱导下载、分享等。 + */ +@property (nonatomic, copy, nullable) NSString *musicOperationUrl; + +/** 付费歌曲相关信息 + * @note 选填,如果歌曲是需要付费的,那么将付费歌曲id等信息封装在内。 + */ +@property (nonatomic, strong) WXMusicVipInfo *musicVipInfo; + +@end + + + +#pragma mark - WXVideoObject +/*! @brief 多媒体消息中包含的视频数据对象 + * + * 微信终端和第三方程序之间传递消息中包含的视频数据对象。 + * @note videoUrl和videoLowBandUrl不能同时为空。 + * @see WXMediaMessage + */ +@interface WXVideoObject : NSObject +/*! @brief 返回一个WXVideoObject对象 + * + * @note 返回的WXVideoObject对象是自动释放的 + */ ++ (WXVideoObject *)object; + +/** 视频网页的url地址 + * @note 长度不能超过10K + */ +@property (nonatomic, copy) NSString *videoUrl; +/** 视频lowband网页的url地址 + * @note 长度不能超过10K + */ +@property (nonatomic, copy) NSString *videoLowBandUrl; + +@end + + + +#pragma mark - WXWebpageObject +/*! @brief 多媒体消息中包含的网页数据对象 + * + * 微信终端和第三方程序之间传递消息中包含的网页数据对象。 + * @see WXMediaMessage + */ +@interface WXWebpageObject : NSObject +/*! @brief 返回一个WXWebpageObject对象 + * + * @note 返回的WXWebpageObject对象是自动释放的 + */ ++ (WXWebpageObject *)object; + +/** 网页的url地址 + * @note 不能为空且长度不能超过10K + */ +@property (nonatomic, copy) NSString *webpageUrl; + +/**是否是私密消息 + */ +@property (nonatomic, assign) BOOL isSecretMessage; + +/** 业务所需的额外信息 */ +@property (nonatomic, strong, nullable) NSDictionary *extraInfoDic; + +@end + + + +#pragma mark - WXAppExtendObject +/*! @brief 多媒体消息中包含的App扩展数据对象 + * + * 第三方程序向微信终端发送包含WXAppExtendObject的多媒体消息, + * 微信需要处理该消息时,会调用该第三方程序来处理多媒体消息内容。 + * @note url,extInfo和fileData不能同时为空 + * @see WXMediaMessage + */ +@interface WXAppExtendObject : NSObject +/*! @brief 返回一个WXAppExtendObject对象 + * + * @note 返回的WXAppExtendObject对象是自动释放的 + */ ++ (WXAppExtendObject *)object; + +/** 若第三方程序不存在,微信终端会打开该url所指的App下载地址 + * @note 长度不能超过10K + */ +@property (nonatomic, copy) NSString *url; +/** 第三方程序自定义简单数据,微信终端会回传给第三方程序处理 + * @note 长度不能超过2K + */ +@property (nonatomic, copy, nullable) NSString *extInfo; +/** App文件数据,该数据发送给微信好友,微信好友需要点击后下载数据,微信终端会回传给第三方程序处理 + * @note 大小不能超过10M + */ +@property (nonatomic, strong, nullable) NSData *fileData; + +@end + + + +#pragma mark - WXEmoticonObject +/*! @brief 多媒体消息中包含的表情数据对象 + * + * 微信终端和第三方程序之间传递消息中包含的表情数据对象。 + * @see WXMediaMessage + */ +@interface WXEmoticonObject : NSObject + +/*! @brief 返回一个WXEmoticonObject对象 + * + * @note 返回的WXEmoticonObject对象是自动释放的 + */ ++ (WXEmoticonObject *)object; + +/** 表情真实数据内容 + * @note 大小不能超过10M + */ +@property (nonatomic, strong) NSData *emoticonData; + +@end + + + +#pragma mark - WXFileObject +/*! @brief 多媒体消息中包含的文件数据对象 + * + * @see WXMediaMessage + */ +@interface WXFileObject : NSObject + +/*! @brief 返回一个WXFileObject对象 + * + * @note 返回的WXFileObject对象是自动释放的 + */ ++ (WXFileObject *)object; + +/** 文件后缀名 + * @note 长度不超过64字节 + */ +@property (nonatomic, copy) NSString *fileExtension; + +/** 文件真实数据内容 + * @note 大小不能超过10M + */ +@property (nonatomic, strong) NSData *fileData; + +@end + + +#pragma mark - WXLocationObject +/*! @brief 多媒体消息中包含的地理位置数据对象 + * + * 微信终端和第三方程序之间传递消息中包含的地理位置数据对象。 + * @see WXMediaMessage + */ +@interface WXLocationObject : NSObject + +/*! @brief 返回一个WXLocationObject对象 + * + * @note 返回的WXLocationObject对象是自动释放的 + */ ++ (WXLocationObject *)object; + +/** 地理位置信息 + * @note 经纬度 + */ +@property (nonatomic, assign) double lng; //经度 +@property (nonatomic, assign) double lat; //纬度 + +@end + +#pragma mark - WXTextObject +/*! @brief 多媒体消息中包含的文本数据对象 + * + * 微信终端和第三方程序之间传递消息中包含的文本数据对象。 + * @see WXMediaMessage + */ +@interface WXTextObject : NSObject + +/*! @brief 返回一个WXTextObject对象 + * + * @note 返回的WXTextObject对象是自动释放的 + */ ++ (WXTextObject *)object; + +/** 地理位置信息 + * @note 文本内容 + */ +@property (nonatomic, copy) NSString *contentText; + +@end + +#pragma mark - WXMiniProgramObject + +@interface WXMiniProgramObject : NSObject + +/*! @brief WXMiniProgramObject对象 + * + * @note 返回的WXMiniProgramObject对象是自动释放的 + */ ++ (WXMiniProgramObject *)object; + +/** 低版本网页链接 + * @attention 长度不能超过1024字节 + */ +@property (nonatomic, copy) NSString *webpageUrl; + +/** 小程序username */ +@property (nonatomic, copy) NSString *userName; + +/** 小程序页面的路径 + * @attention 不填默认拉起小程序首页 + */ +@property (nonatomic, copy, nullable) NSString *path; + +/** 小程序新版本的预览图 + * @attention 大小不能超过128k + */ +@property (nonatomic, strong, nullable) NSData *hdImageData; + +/** 是否使用带 shareTicket 的转发 */ +@property (nonatomic, assign) BOOL withShareTicket; + +/** 分享小程序的版本 + * @attention (正式,开发,体验) + */ +@property (nonatomic, assign) WXMiniProgramType miniProgramType; + +/** 是否禁用转发 */ +@property (nonatomic, assign) BOOL disableForward; + +@property (nonatomic, assign) BOOL isUpdatableMessage; + +@property (nonatomic, assign) BOOL isSecretMessage; + + +/** 业务所需的额外信息 */ +@property (nonatomic, strong, nullable) NSDictionary *extraInfoDic; + +@end + +#pragma mark - WXGameLiveObject + +/*! @brief WXGameLiveObject对象 + * + * @note 游戏直播消息类型 + */ + +@interface WXGameLiveObject : NSObject + ++ (WXGameLiveObject *)object; + +/** 业务所需的额外信息 */ +@property (nonatomic, strong, nullable) NSDictionary *extraInfoDic; + +@end + +@interface WXNativeGamePageObject : NSObject + +/** 是否为视频类型 + */ +@property (nonatomic, assign) BOOL isVideo; + +/** 视频时长 + @note 当为视频类型时,必填;单位为秒 + */ +@property (nonatomic, assign) UInt32 videoDuration; + +/** 透传字段 + @note 长度限制为100K + */ +@property (nonatomic, copy) NSString *shareData; + +/** 缩略图 + @note 大小限制为256K + */ +@property (nonatomic, strong) NSData *gameThumbData; + ++ (WXNativeGamePageObject *)object; + +@end + +#pragma mark - WXLaunchMiniProgramReq + +/*! @brief WXLaunchMiniProgramReq对象, 可实现通过sdk拉起微信小程序 + * + * @note 返回的WXLaunchMiniProgramReq对象是自动释放的 + */ +@interface WXLaunchMiniProgramReq : BaseReq + ++ (WXLaunchMiniProgramReq *)object; + +/** 小程序username */ +@property (nonatomic, copy) NSString *userName; + +/** 小程序页面的路径 + * @attention 不填默认拉起小程序首页 + */ +@property (nonatomic, copy, nullable) NSString *path; + +/** 分享小程序的版本 + * @attention (正式,开发,体验) + */ +@property (nonatomic, assign) WXMiniProgramType miniProgramType; + +/** ext信息 + * @attention json格式 + */ +@property (nonatomic, copy, nullable) NSString *extMsg; + +/** extDic + * @attention 字典,可存放图片等比较大的数据 + */ +@property (nonatomic, copy, nullable) NSDictionary *extDic; + +@end + +#pragma mark - WXLaunchMiniProgramResp +/*! @brief 微信终端向第三方程序返回的WXLaunchMiniProgramReq处理结果。 + * + * 第三方程序向微信终端发送WXLaunchMiniProgramReq后,微信发送回来的处理结果,该结果用WXLaunchMiniProgramResp表示。 + */ +@interface WXLaunchMiniProgramResp : BaseResp + +@property (nonatomic, copy, nullable) NSString *extMsg; + +@end + + +#pragma mark - WXOpenBusinessViewReq +/*! @brief WXOpenBusinessViewReq对象, 可实现第三方通知微信启动,打开业务页面 + * + * @note 返回的WXOpenBusinessViewReq对象是自动释放的 + */ + +@interface WXOpenBusinessViewReq : BaseReq + ++ (WXOpenBusinessViewReq *)object; + +/** 业务类型 + */ +@property (nonatomic, copy) NSString *businessType; + +/** 业务参数 + */ +@property (nonatomic, copy, nullable) NSString *query; + +/** ext信息 + * @note 选填,json格式 + */ +@property (nonatomic, copy, nullable) NSString *extInfo; + +/** extData数据 + * @note + */ +@property (nonatomic, strong, nullable) NSData *extData; +@end + + +@interface WXOpenBusinessViewResp : BaseResp + +/** 业务类型 + */ +@property (nonatomic, copy) NSString *businessType; + +/** 业务返回数据 + */ +@property (nonatomic, copy, nullable) NSString *extMsg; + +@end + +#pragma mark - WXOpenCustomerServiceReq +@interface WXOpenCustomerServiceReq : BaseReq + ++ (WXOpenCustomerServiceReq *)object; + +/**企微客服发起流程 url + */ +@property (nonatomic, copy, nullable) NSString *url; + +/**企业 id + */ +@property (nonatomic, copy, nullable) NSString *corpid; + +@end + +@interface WXOpenCustomerServiceResp : BaseResp + +/** 业务返回数据 + */ +@property (nonatomic, copy, nullable) NSString *extMsg; + +@end + + +#pragma mark - WXChannelStartLiveReq + +@interface WXChannelStartLiveReq : BaseReq + ++ (WXChannelStartLiveReq *)object; + +/** 必填,直播业务数据(json格式) + */ +@property (nonatomic, copy) NSString *liveJsonInfo; + +@end + +@interface WXChannelStartLiveResp : BaseResp + +/** 业务返回数据 + */ +@property (nonatomic, copy, nullable) NSString *extMsg; + +@end + +NS_ASSUME_NONNULL_END diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Headers/WechatAuthSDK.h b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Headers/WechatAuthSDK.h new file mode 100644 index 0000000..308aaea --- /dev/null +++ b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Headers/WechatAuthSDK.h @@ -0,0 +1,68 @@ +// +// WechatAuthSDK.h +// WechatAuthSDK +// +// Created by 李凯 on 13-11-29. +// Copyright (c) 2013年 Tencent. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +enum AuthErrCode { + WechatAuth_Err_Ok = 0, //Auth成功 + WechatAuth_Err_NormalErr = -1, //普通错误 + WechatAuth_Err_NetworkErr = -2, //网络错误 + WechatAuth_Err_GetQrcodeFailed = -3, //获取二维码失败 + WechatAuth_Err_Cancel = -4, //用户取消授权 + WechatAuth_Err_Timeout = -5, //超时 +}; + +@protocol WechatAuthAPIDelegate +@optional + +- (void)onAuthGotQrcode:(UIImage *)image; //得到二维码 +- (void)onQrcodeScanned; //二维码被扫描 +- (void)onAuthFinish:(int)errCode AuthCode:(nullable NSString *)authCode; //成功登录 + +@end + +@interface WechatAuthSDK : NSObject{ + NSString *_sdkVersion; + __weak id _delegate; +} + +@property(nonatomic, weak, nullable) id delegate; +@property(nonatomic, readonly) NSString *sdkVersion; //authSDK版本号 + +/*! @brief 发送登录请求,等待WechatAuthAPIDelegate回调 + * + * @param appId 微信开发者ID + * @param nonceStr 一个随机的尽量不重复的字符串,用来使得每次的signature不同 + * @param timeStamp 时间戳 + * @param scope 应用授权作用域,拥有多个作用域用逗号(,)分隔 + * @param signature 签名 + * @param schemeData 会在扫码后拼在scheme后 + * @return 成功返回YES,失败返回NO + 注:该实现只保证同时只有一个Auth在运行,Auth未完成或未Stop再次调用Auth接口时会返回NO。 + */ + +- (BOOL)Auth:(NSString *)appId + nonceStr:(NSString *)nonceStr + timeStamp:(NSString *)timeStamp + scope:(NSString *)scope + signature:(NSString *)signature + schemeData:(nullable NSString *)schemeData; + + +/*! @brief 暂停登录请求 + * + * @return 成功返回YES,失败返回NO。 + */ +- (BOOL)StopAuth; + +@end + +NS_ASSUME_NONNULL_END diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Headers/WechatOpenSDK.h b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Headers/WechatOpenSDK.h new file mode 100644 index 0000000..979e5e9 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Headers/WechatOpenSDK.h @@ -0,0 +1,9 @@ +// WechatOpenSDK.h +// +// Created by Wechat. +// Copyright (c) 2012年 Tencent. All rights reserved. +// +#import +#import +#import + diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Info.plist b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Info.plist new file mode 100755 index 0000000..45d6068 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Info.plist @@ -0,0 +1,15 @@ + + + + CFBundleIdentifier + com.tencent.WechatOpenSDK + CFBundleVersion + 1.0.0 + CFBundleShortVersionString + 1.0 + MinimumOSVersion + 12.0 + CFBundleExecutable + WechatOpenSDK + + \ No newline at end of file diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Modules/module.modulemap b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Modules/module.modulemap new file mode 100644 index 0000000..3ddbeb0 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module WechatOpenSDK { + umbrella header "WechatOpenSDK.h" + export * + link "WechatOpenSDK" +} diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/WechatOpenSDK b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/WechatOpenSDK new file mode 100644 index 0000000..defd024 Binary files /dev/null and b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64/WechatOpenSDK.framework/WechatOpenSDK differ diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Headers/WXApi.h b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Headers/WXApi.h new file mode 100644 index 0000000..879c505 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Headers/WXApi.h @@ -0,0 +1,231 @@ +// +// WXApi.h +// 所有Api接口 +// +// Created by Wechat on 12-2-28. +// Copyright (c) 2012年 Tencent. All rights reserved. +// + +#import +#import "WXApiObject.h" + +NS_ASSUME_NONNULL_BEGIN + + +typedef BOOL(^WXGrantReadPasteBoardPermissionCompletion)(void); + + +#pragma mark - WXApiDelegate +/*! @brief 接收并处理来自微信终端程序的事件消息 + * + * 接收并处理来自微信终端程序的事件消息,期间微信界面会切换到第三方应用程序。 + * WXApiDelegate 会在handleOpenURL:delegate:中使用并触发。 + */ +@protocol WXApiDelegate +@optional + +/*! @brief 收到一个来自微信的请求,第三方应用程序处理完后调用sendResp向微信发送结果 + * + * 收到一个来自微信的请求,异步处理完成后必须调用sendResp发送处理结果给微信。 + * 可能收到的请求有GetMessageFromWXReq、ShowMessageFromWXReq等。 + * @param req 具体请求内容,是自动释放的 + */ +- (void)onReq:(BaseReq*)req; + + + +/*! @brief 发送一个sendReq后,收到微信的回应 + * + * 收到一个来自微信的处理结果。调用一次sendReq后会收到onResp。 + * 可能收到的处理结果有SendMessageToWXResp、SendAuthResp等。 + * @param resp具体的回应内容,是自动释放的 + */ +- (void)onResp:(BaseResp*)resp; + +/* ! @brief 用于在iOS16以及以上系统上,控制OpenSDK是否读取剪切板中微信传递的数据以及读取的时机 + * 在iOS16以及以上系统,在SDK需要读取剪切板中微信写入的数据时,会回调该方法。没有实现默认会直接读取微信通过剪切板传递过来的数据 + * 注意: + * 1. 只在iOS16以及以上的系统版本上回调; + * 2. 不实现时,OpenSDK会直接调用读取剪切板接口,读取微信传递过来的数据; + * 3. 若实现该方法:开发者需要通过调用completion(), 支持异步,通知SDK允许读取剪切板中微信传递的数据, + * 不调用completion()则代表不授权OpenSDK读取剪切板,会导致收不到onReq:, onResp:回调,无法后续业务流程。请谨慎使用 + * 4. 不要长时间持有completion不释放,可能会导致内存泄漏。 + */ +- (void)onNeedGrantReadPasteBoardPermissionWithURL:(nonnull NSURL *)openURL completion:(nonnull WXGrantReadPasteBoardPermissionCompletion)completion; + +@end + +#pragma mark - WXApiLogDelegate + +@protocol WXApiLogDelegate + +- (void)onLog:(NSString*)log logLevel:(WXLogLevel)level; + +@end + + +#pragma mark - WXApi + +/*! @brief 微信Api接口函数类 + * + * 该类封装了微信终端SDK的所有接口 + */ +@interface WXApi : NSObject + +/*! @brief WXApi的成员函数,向微信终端程序注册第三方应用。 + * + * 需要在每次启动第三方应用程序时调用。 + * @attention 请保证在主线程中调用此函数 + * @param appid 微信开发者ID + * @param universalLink 微信开发者Universal Link + * @return 成功返回YES,失败返回NO。 + */ ++ (BOOL)registerApp:(NSString *)appid universalLink:(NSString *)universalLink; + + +/*! @brief 处理旧版微信通过URL启动App时传递的数据 + * + * 需要在 application:openURL:sourceApplication:annotation:或者application:handleOpenURL中调用。 + * @param url 微信启动第三方应用时传递过来的URL + * @param delegate WXApiDelegate对象,用来接收微信触发的消息。 + * @return 成功返回YES,失败返回NO。 + */ ++ (BOOL)handleOpenURL:(NSURL *)url delegate:(nullable id)delegate; + + +/*! @brief 处理微信通过Universal Link启动App时传递的数据 + * + * 需要在 application:continueUserActivity:restorationHandler:中调用。 + * @param userActivity 微信启动第三方应用时系统API传递过来的userActivity + * @param delegate WXApiDelegate对象,用来接收微信触发的消息。 + * @return 成功返回YES,失败返回NO。 + */ ++ (BOOL)handleOpenUniversalLink:(NSUserActivity *)userActivity delegate:(nullable id)delegate; + + +/*! @brief 检查微信是否已被用户安装 + * + * @return 微信已安装返回YES,未安装返回NO。 + */ ++ (BOOL)isWXAppInstalled; + + + +/*! @brief 判断当前微信的版本是否支持OpenApi + * + * @return 支持返回YES,不支持返回NO。 + */ ++ (BOOL)isWXAppSupportApi; + + +/*! @brief 判断当前微信的版本是否支持分享微信状态功能 + * + * @attention 需在工程LSApplicationQueriesSchemes配置中添加weixinStateAPI + * @return 支持返回YES,不支持返回NO。 + */ ++ (BOOL)isWXAppSupportStateAPI; + + +#ifndef BUILD_WITHOUT_PAY +/*! @brief 判断当前微信的版本是否支持二维码拉起微信支付 + * + * @attention 需在工程LSApplicationQueriesSchemes配置中添加weixinQRCodePayAPI + * @return 支持返回YES,不支持返回NO。 + */ ++ (BOOL)isWXAppSupportQRCodePayAPI; + +#endif + + +/*! @brief 获取微信的itunes安装地址 + * + * @return 微信的安装地址字符串。 + */ ++ (NSString *)getWXAppInstallUrl; + + + +/*! @brief 获取当前微信SDK的版本号 + * + * @return 返回当前微信SDK的版本号 + */ ++ (NSString *)getApiVersion; + + + +/*! @brief 打开微信 + * + * @return 成功返回YES,失败返回NO。 + */ ++ (BOOL)openWXApp; + + + +/*! @brief 发送请求到微信,等待微信返回onResp + * + * 函数调用后,会切换到微信的界面。第三方应用程序等待微信返回onResp。微信在异步处理完成后一定会调用onResp。支持以下类型 + * SendAuthReq、SendMessageToWXReq、PayReq等。 + * @param req 具体的发送请求。 + * @param completion 调用结果回调block + */ ++ (void)sendReq:(BaseReq *)req completion:(void (^ __nullable)(BOOL success))completion; + +/*! @brief 收到微信onReq的请求,发送对应的应答给微信,并切换到微信界面 + * + * 函数调用后,会切换到微信的界面。第三方应用程序收到微信onReq的请求,异步处理该请求,完成后必须调用该函数。可能发送的相应有 + * GetMessageFromWXResp、ShowMessageFromWXResp等。 + * @param resp 具体的应答内容 + * @param completion 调用结果回调block + */ ++ (void)sendResp:(BaseResp*)resp completion:(void (^ __nullable)(BOOL success))completion; + + +/*! @brief 发送Auth请求到微信,支持用户没安装微信,等待微信返回onResp + * + * 函数调用后,会切换到微信的界面。第三方应用程序等待微信返回onResp。微信在异步处理完成后一定会调用onResp。支持SendAuthReq类型。 + * @param req 具体的发送请求。 + * @param viewController 当前界面对象。 + * @param delegate WXApiDelegate对象,用来接收微信触发的消息。 + * @param completion 调用结果回调block + */ ++ (void)sendAuthReq:(SendAuthReq *)req viewController:(UIViewController*)viewController delegate:(nullable id)delegate completion:(void (^ __nullable)(BOOL success))completion; + + +/*! @brief 测试函数,用于排查当前App通过Universal Link方式分享到微信的流程 + 注意1: 调用自检函数之前必须要先调用registerApp:universalLink接口, 并确认调用成功 + 注意2: 自检过程中会有Log产生,可以先调用startLogByLevel函数,根据Log排查问题 + 注意3: 会多次回调block + 注意4: 仅用于新接入SDK时调试使用,请勿在正式环境的调用 + * + * 当completion回调的step为WXULCheckStepFinal时,表示检测通过,Universal Link接入成功 + * @param completion 回调Block + */ ++ (void)checkUniversalLinkReady:(nonnull WXCheckULCompletion)completion; + + +/*! @brief WXApi的成员函数,接受微信的log信息。byBlock + 注意1:SDK会强引用这个block,注意不要导致内存泄漏,注意不要导致内存泄漏 + 注意2:调用过一次startLog by block之后,如果再调用一次任意方式的startLoad,会释放上一次logBlock,不再回调上一个logBlock + * + * @param level 打印log的级别 + * @param logBlock 打印log的回调block + */ + ++ (void)startLogByLevel:(WXLogLevel)level logBlock:(WXLogBolock)logBlock; + +/*! @brief WXApi的成员函数,接受微信的log信息。byDelegate + 注意1:sdk会弱引用这个delegate,这里可加任意对象为代理,不需要与WXApiDelegate同一个对象 + 注意2:调用过一次startLog by delegate之后,再调用一次任意方式的startLoad,不会再回调上一个logDelegate对象 + * @param level 打印log的级别 + * @param logDelegate 打印log的回调代理, + */ ++ (void)startLogByLevel:(WXLogLevel)level logDelegate:(id)logDelegate; + +/*! @brief 停止打印log,会清理block或者delegate为空,释放block + * @param + */ ++ (void)stopLog; + +@end + +NS_ASSUME_NONNULL_END diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Headers/WXApiObject.h b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Headers/WXApiObject.h new file mode 100644 index 0000000..85b6457 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Headers/WXApiObject.h @@ -0,0 +1,1410 @@ +// +// MMApiObject.h +// Api对象,包含所有接口和对象数据定义 +// +// Created by Wechat on 12-2-28. +// Copyright (c) 2012年 Tencent. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/*! @brief 错误码 + * + */ +enum WXErrCode { + WXSuccess = 0, /**< 成功 */ + WXErrCodeCommon = -1, /**< 普通错误类型 */ + WXErrCodeUserCancel = -2, /**< 用户点击取消并返回 */ + WXErrCodeSentFail = -3, /**< 发送失败 */ + WXErrCodeAuthDeny = -4, /**< 授权失败 */ + WXErrCodeUnsupport = -5, /**< 微信不支持 */ +}; + + + +/*! @brief 请求发送场景 + * + */ +enum WXScene { + WXSceneSession = 0, /**< 聊天界面 */ + WXSceneTimeline = 1, /**< 朋友圈 */ + WXSceneFavorite = 2, /**< 收藏 */ + WXSceneSpecifiedSession = 3, /**< 指定联系人 */ + WXSceneState = 4, /**< 状态 */ +}; + + +enum WXAPISupport { + WXAPISupportSession = 0, +}; + + + +/*! @brief 跳转profile类型 + * + */ +enum WXBizProfileType { + WXBizProfileType_Normal = 0, //**< 普通公众号 */ + WXBizProfileType_Device = 1, //**< 硬件公众号 */ +}; + +/*! @brief 分享小程序类型 + * + */ +typedef NS_ENUM(NSUInteger, WXMiniProgramType) { + WXMiniProgramTypeRelease = 0, //**< 正式版 */ + WXMiniProgramTypeTest = 1, //**< 开发版 */ + WXMiniProgramTypePreview = 2, //**< 体验版 */ +}; + +/*! @brief 跳转mp网页类型 + * + */ +enum WXMPWebviewType { + WXMPWebviewType_Ad = 0, /**< 广告网页 **/ +}; + +/*! @brief log的级别 + * + */ +typedef NS_ENUM(NSInteger,WXLogLevel) { + WXLogLevelNormal = 0, // 打印日常的日志 + WXLogLevelDetail = 1, // 打印详细的日志 +}; + + +/*! @brief 打印回调的block + * + */ +typedef void(^WXLogBolock)(NSString *log); + +/*! @brief 微信Universal Link检查函数 (WXApi#checkUniversalLinkReady:),检查步骤枚举值 + * + * WXULCheckStepParams 参数检测 + * WXULCheckStepSystemVersion 当前系统版本检测 + * WXULCheckStepWechatVersion 微信客户端版本检测 + * WXULCheckStepSDKInnerOperation 微信SDK内部操作检测 + * WXULCheckStepLaunchWechat App拉起微信检测 + * WXULCheckStepBackToCurrentApp 由微信返回当前App检测 + * WXULCheckStepFinal 最终结果 + */ +typedef NS_ENUM(NSInteger, WXULCheckStep) +{ + WXULCheckStepParams, + WXULCheckStepSystemVersion, + WXULCheckStepWechatVersion, + WXULCheckStepSDKInnerOperation, + WXULCheckStepLaunchWechat, + WXULCheckStepBackToCurrentApp, + WXULCheckStepFinal, +}; + + +#pragma mark - WXCheckULStepResult + +/*! @brief 该类为微信Universal Link检测函数结果类 +* +*/ +@interface WXCheckULStepResult : NSObject + +/** 是否成功 */ +@property(nonatomic, assign) BOOL success; +/** 当前错误信息 */ +@property(nonatomic, strong) NSString* errorInfo; +/** 修正建议 */ +@property(nonatomic, strong) NSString* suggestion; + +- (instancetype)initWithCheckResult:(BOOL)success errorInfo:(nullable NSString*)errorInfo suggestion:(nullable NSString*)suggestion; + +@end + + +/*! @brief 微信Universal Link检查函数 (WXApi#checkUniversalLinkReady:),回调Block + * + * @param step 当前检测步骤 + * @param result 检测结果 + */ +typedef void(^WXCheckULCompletion)(WXULCheckStep step, WXCheckULStepResult* result); + + +#pragma mark - BaseReq +/*! @brief 该类为微信终端SDK所有请求类的基类 + * + */ +@interface BaseReq : NSObject + +/** 请求类型 */ +@property (nonatomic, assign) int type; +/** 由用户微信号和AppID组成的唯一标识,需要校验微信用户是否换号登录时填写*/ +@property (nonatomic, copy) NSString *openID; + +@end + + + +#pragma mark - BaseResp +/*! @brief 该类为微信终端SDK所有响应类的基类 + * + */ +@interface BaseResp : NSObject +/** 错误码 */ +@property (nonatomic, assign) int errCode; +/** 错误提示字符串 */ +@property (nonatomic, copy) NSString *errStr; +/** 响应类型 */ +@property (nonatomic, assign) int type; + +@end + + + +#pragma mark - WXMediaMessage +@class WXMediaMessage; + +#ifndef BUILD_WITHOUT_PAY + +#pragma mark - PayReq +/*! @brief 第三方向微信终端发起支付的消息结构体 + * + * 第三方向微信终端发起支付的消息结构体,微信终端处理后会向第三方返回处理结果 + * @see PayResp + */ +@interface PayReq : BaseReq + +/** 商家向财付通申请的商家id */ +@property (nonatomic, copy) NSString *partnerId; +/** 预支付订单 */ +@property (nonatomic, copy) NSString *prepayId; +/** 随机串,防重发 */ +@property (nonatomic, copy) NSString *nonceStr; +/** 时间戳,防重发 */ +@property (nonatomic, assign) UInt32 timeStamp; +/** 商家根据财付通文档填写的数据和签名 */ +@property (nonatomic, copy) NSString *package; +/** 商家根据微信开放平台文档对数据做的签名 */ +@property (nonatomic, copy) NSString *sign; + +@end + + +#pragma mark - PayResp +/*! @brief 微信终端返回给第三方的关于支付结果的结构体 + * + * 微信终端返回给第三方的关于支付结果的结构体 + */ +@interface PayResp : BaseResp + +/** 财付通返回给商家的信息 */ +@property (nonatomic, copy) NSString *returnKey; + +@end + +#pragma mark - WXOfflinePay +/*! @brief 第三方向微信终端发起离线支付 + * + * 第三方向微信终端发起离线支付的消息结构体 + */ +@interface WXOfflinePayReq : BaseReq + +@end + +/*! @brief 第三方向微信终端发起离线支付返回 + * + * 第三方向微信终端发起离线支付返回的消息结构体 + */ +@interface WXOfflinePayResp : BaseResp + +@end + + +#pragma mark - WXNontaxPayReq +@interface WXNontaxPayReq:BaseReq + +@property (nonatomic, copy) NSString *urlString; + +@end + +#pragma mark - WXNontaxPayResp +@interface WXNontaxPayResp : BaseResp + +@property (nonatomic, copy) NSString *wxOrderId; + +@end + +#pragma mark - WXPayInsuranceReq +@interface WXPayInsuranceReq : BaseReq + +@property (nonatomic, copy) NSString *urlString; + +@end + +#pragma mark - WXPayInsuranceResp +@interface WXPayInsuranceResp : BaseResp + +@property (nonatomic, copy) NSString *wxOrderId; + +@end + + +#pragma mark - WXQRCodePayReq +@interface WXQRCodePayReq : BaseReq +/** 码内容 + * @note 必填,码长度必须大于0且小于10K + */ +@property (nonatomic, copy) NSString *codeContent; +/** 额外信息 + * @note 长度必须大于0且小于10K + */ +@property (nonatomic, copy) NSString *extraMsg; + +@end + +@interface WXQRCodePayResp : BaseResp +@end + +#endif + + +#pragma mark - SendAuthReq +/*! @brief 第三方程序向微信终端请求认证的消息结构 + * + * 第三方程序要向微信申请认证,并请求某些权限,需要调用WXApi的sendReq成员函数, + * 向微信终端发送一个SendAuthReq消息结构。微信终端处理完后会向第三方程序发送一个处理结果。 + * @see SendAuthResp + */ +@interface SendAuthReq : BaseReq +/** 第三方程序要向微信申请认证,并请求某些权限,需要调用WXApi的sendReq成员函数,向微信终端发送一个SendAuthReq消息结构。微信终端处理完后会向第三方程序发送一个处理结果。 + * @see SendAuthResp + * @note scope字符串长度不能超过1K + */ +@property (nonatomic, copy) NSString *scope; +/** 第三方程序本身用来标识其请求的唯一性,最后跳转回第三方程序时,由微信终端回传。 + * @note state字符串长度不能超过1K + */ +@property (nonatomic, copy) NSString *state; + +@property (nonatomic, assign) BOOL isOption1; + +/** 是否关闭自动授权 + * @note YES为关闭自动授权,每次登陆都需要用户手动授权;NO为允许自动授权 + */ +@property (nonatomic, assign) BOOL nonautomatic; + +@property (nonatomic, copy) NSString *extData; + +@end + +#pragma mark - SendAuthResp +/*! @brief 微信处理完第三方程序的认证和权限申请后向第三方程序回送的处理结果。 + * + * 第三方程序要向微信申请认证,并请求某些权限,需要调用WXApi的sendReq成员函数,向微信终端发送一个SendAuthReq消息结构。 + * 微信终端处理完后会向第三方程序发送一个SendAuthResp。 + * @see onResp + */ +@interface SendAuthResp : BaseResp +@property (nonatomic, copy, nullable) NSString *code; +/** 第三方程序发送时用来标识其请求的唯一性的标志,由第三方程序调用sendReq时传入,由微信终端回传 + * @note state字符串长度不能超过1K + * @note 在复杂度较高的应用程序中,可能会出现其他模块请求的响应对象被错误地回调到当前模块。 + * 为了避免影响其他模块,建议当前模块的开发者根据SendAuthResp的内容,采用白名单的方式进行处理。 + * 例如,SendAuthResp.state符合预期时,才对其进行处理。 + */ +@property (nonatomic, copy, nullable) NSString *state; +@property (nonatomic, copy, nullable) NSString *lang; +@property (nonatomic, copy, nullable) NSString *country; +@end + +#pragma mark - WXStateJumpInfo +/*! @brief 状态发表时的小尾巴跳转信息 + */ +@interface WXStateJumpInfo : NSObject + +@end + +#pragma mark - WXStateJumpUrlInfo +/*! @brief 状态小尾巴跳转指定url的信息 + */ +@interface WXStateJumpUrlInfo : WXStateJumpInfo +/** 跳转到指定的url + * @note 必填,url长度必须大于0且小于10K + */ +@property (nonatomic, copy) NSString *url; + +@end + +#pragma mark - WXStateJumpWXMiniProgramInfo +/*! @brief 状态小尾巴跳转指定小程序的信息 + */ +@interface WXStateJumpMiniProgramInfo : WXStateJumpInfo +/** 小程序username + * @note 必填 + */ +@property (nonatomic, copy) NSString *username; + +/** 小程序页面的路径 + * @attention 不填默认拉起小程序首页 + */ +@property (nonatomic, copy, nullable) NSString *path; + +/** 分享小程序的版本 + * @attention (正式,开发,体验) + */ +@property (nonatomic, assign) WXMiniProgramType miniProgramType; + +@end + + + +#pragma mark - WXStateJumpWXMiniProgramInfo +/*! @brief 状态小尾巴跳转指定视频号主页信息 + */ +@interface WXStateJumpChannelProfileInfo : WXStateJumpInfo +/** 视频号username + * @note 必填,username长度必须大于0且小于1K + */ +@property (nonatomic, copy) NSString *username; + + +@end + +#pragma mark - WXStateSceneDataObject +/*! @brief 场景类型额外参数基类 + */ +@interface WXSceneDataObject : NSObject + +@end + +#pragma mark - WXStateSceneDataObject +/*! @brief 状态场景类型 + * 用户填写WXStateSceneDataObject参数后,可以跳转到微信状态发表页 + */ +@interface WXStateSceneDataObject : WXSceneDataObject + +/** 状态标志的ID + * @note 选填,文本长度必须小于10K + */ +@property (nonatomic, copy) NSString *stateId; +/** 状态发表时附带的文本描述 + * @note 选填,文本长度必须小于10K + */ +@property (nonatomic, copy) NSString *stateTitle; +/** 后台校验token + * @note 选填,文本长度必须小于10K + */ +@property (nonatomic, copy) NSString *token; +/** 小尾巴跳转所需的信息 + * @note 必填,目前仅支持url跳转 + */ +@property (nonatomic, strong) WXStateJumpInfo *stateJumpDataInfo; + +@end + +#pragma mark - SendMessageToWXReq +/*! @brief 第三方程序发送消息至微信终端程序的消息结构体 + * + * 第三方程序向微信发送信息需要传入SendMessageToWXReq结构体,信息类型包括文本消息和多媒体消息, + * 分别对应于text和message成员。调用该方法后,微信处理完信息会向第三方程序发送一个处理结果。 + * @see SendMessageToWXResp + */ +@interface SendMessageToWXReq : BaseReq +/** 发送消息的文本内容 + * @note 文本长度必须大于0且小于10K + */ +@property (nonatomic, copy) NSString *text; +/** 发送消息的多媒体内容 + * @see WXMediaMessage + */ +@property (nonatomic, strong) WXMediaMessage *message; +/** 发送消息的类型,包括文本消息和多媒体消息两种,两者只能选择其一,不能同时发送文本和多媒体消息 */ +@property (nonatomic, assign) BOOL bText; +/** 发送的目标场景,可以选择发送到会话(WXSceneSession)或者朋友圈(WXSceneTimeline)。 默认发送到会话。 + * @see WXScene + */ +@property (nonatomic, assign) int scene; +/** 指定发送消息的人 + * @note WXSceneSpecifiedSession时有效 + */ +@property (nonatomic, copy, nullable) NSString *toUserOpenId; +/** 目标场景附带信息 + * @note 目前只针对状态场景 + */ +@property (nonatomic, strong) WXSceneDataObject *sceneDataObject; + +@end + +#pragma mark - SendMessageToWXResp +/*! @brief 微信终端向第三方程序返回的SendMessageToWXReq处理结果。 + * + * 第三方程序向微信终端发送SendMessageToWXReq后,微信发送回来的处理结果,该结果用SendMessageToWXResp表示。 + */ +@interface SendMessageToWXResp : BaseResp +@property(nonatomic, copy) NSString *lang; +@property(nonatomic, copy) NSString *country; +@end + +#pragma mark - GetMessageFromWXReq +/*! @brief 微信终端向第三方程序请求提供内容的消息结构体。 + * + * 微信终端向第三方程序请求提供内容,微信终端会向第三方程序发送GetMessageFromWXReq消息结构体, + * 需要第三方程序调用sendResp返回一个GetMessageFromWXResp消息结构体。 + */ +@interface GetMessageFromWXReq : BaseReq +@property (nonatomic, strong) NSString *lang; +@property (nonatomic, strong) NSString *country; +@end + + + +#pragma mark - GetMessageFromWXResp +/*! @brief 微信终端向第三方程序请求提供内容,第三方程序向微信终端返回的消息结构体。 + * + * 微信终端向第三方程序请求提供内容,第三方程序调用sendResp向微信终端返回一个GetMessageFromWXResp消息结构体。 + */ +@interface GetMessageFromWXResp : BaseResp +/** 向微信终端提供的文本内容 + @note 文本长度必须大于0且小于10K + */ +@property (nonatomic, strong) NSString *text; +/** 向微信终端提供的多媒体内容。 + * @see WXMediaMessage + */ +@property (nonatomic, strong) WXMediaMessage *message; +/** 向微信终端提供内容的消息类型,包括文本消息和多媒体消息两种,两者只能选择其一,不能同时发送文本和多媒体消息 */ +@property (nonatomic, assign) BOOL bText; +@end + + + +#pragma mark - ShowMessageFromWXReq +/*! @brief 微信通知第三方程序,要求第三方程序显示的消息结构体。 + * + * 微信需要通知第三方程序显示或处理某些内容时,会向第三方程序发送ShowMessageFromWXReq消息结构体。 + * 第三方程序处理完内容后调用sendResp向微信终端发送ShowMessageFromWXResp。 + */ +@interface ShowMessageFromWXReq : BaseReq +/** 微信终端向第三方程序发送的要求第三方程序处理的多媒体内容 + * @see WXMediaMessage + */ +@property (nonatomic, strong) WXMediaMessage *message; +@property (nonatomic, copy) NSString *lang; +@property (nonatomic, copy) NSString *country; +@end + + + +#pragma mark - ShowMessageFromWXResp +/*! @brief 微信通知第三方程序,要求第三方程序显示或处理某些消息,第三方程序处理完后向微信终端发送的处理结果。 + * + * 微信需要通知第三方程序显示或处理某些内容时,会向第三方程序发送ShowMessageFromWXReq消息结构体。 + * 第三方程序处理完内容后调用sendResp向微信终端发送ShowMessageFromWXResp。 + */ +@interface ShowMessageFromWXResp : BaseResp +@end + + +#pragma mark - LaunchFromWXReq +/*! @brief 微信终端打开第三方程序携带的消息结构体 + * + * 微信向第三方发送的结构体,第三方不需要返回 + */ +@interface LaunchFromWXReq : BaseReq +@property (nonatomic, strong) WXMediaMessage *message; +@property (nonatomic, copy) NSString *lang; +@property (nonatomic, copy) NSString *country; +@end + + +#pragma mark - OpenWebviewReq +/* ! @brief 第三方通知微信启动内部浏览器,打开指定网页 + * + * 第三方通知微信启动内部浏览器,打开指定Url对应的网页 + */ +@interface OpenWebviewReq : BaseReq +/** 需要打开的网页对应的Url + * @attention 长度不能超过1024 + */ +@property(nonatomic, copy) NSString *url; + +@end + +#pragma mark - OpenWebviewResp +/*! @brief 微信终端向第三方程序返回的OpenWebviewReq处理结果 + * + * 第三方程序向微信终端发送OpenWebviewReq后,微信发送回来的处理结果,该结果用OpenWebviewResp表示 + */ +@interface OpenWebviewResp : BaseResp + +@end + + + +#pragma mark - WXOpenBusinessWebViewReq +/*! @brief 第三方通知微信启动内部浏览器,打开指定业务的网页 + * + * + */ +@interface WXOpenBusinessWebViewReq : BaseReq + +/** 网页业务类型 + * @attention + */ +@property (nonatomic, assign) UInt32 businessType; + +/** 网页业务参数 + * @attention + */ +@property (nonatomic, strong, nullable) NSDictionary *queryInfoDic; + +@end + +#pragma mark - WXOpenBusinessWebViewResp +/*! @brief 微信终端向第三方程序返回的WXOpenBusinessWebViewResp处理结果。 + * + * 第三方程序向微信终端发送WXOpenBusinessWebViewReq后,微信发送回来的处理结果,该结果用WXOpenBusinessWebViewResp表示。 + */ +@interface WXOpenBusinessWebViewResp : BaseResp +/** 第三方程序自定义简单数据,微信终端会回传给第三方程序处理 + * @attention 长度不能超过2k + */ +@property (nonatomic, copy) NSString *result; + +/** 网页业务类型 + * @attention + */ +@property (nonatomic, assign) UInt32 businessType; + +@end + + +#pragma mark - OpenRankListReq +/* ! @brief 第三方通知微信,打开硬件排行榜 + * + * 第三方通知微信,打开硬件排行榜 + */ +@interface OpenRankListReq : BaseReq + +@end + +#pragma mark - OpenRanklistResp +/*! @brief 微信终端向第三方程序返回的OpenRankListReq处理结果。 + * + * 第三方程序向微信终端发送OpenRankListReq后,微信发送回来的处理结果,该结果用OpenRankListResp表示。 + */ +@interface OpenRankListResp : BaseResp + +@end + + +#pragma mark - WXCardItem + +@interface WXCardItem : NSObject +/** 卡id + * @attention 长度不能超过1024字节 + */ +@property (nonatomic, copy) NSString *cardId; +/** ext信息 + * @attention 长度不能超过2024字节 + */ +@property (nonatomic, copy, nullable) NSString *extMsg; +/** + * @attention 卡的状态,req不需要填。resp:0为未添加,1为已添加。 + */ +@property (nonatomic, assign) UInt32 cardState; +/** + * @attention req不需要填,chooseCard返回的。 + */ +@property (nonatomic, copy) NSString *encryptCode; +/** + * @attention req不需要填,chooseCard返回的。 + */ +@property (nonatomic, copy) NSString *appID; +@end; + +#pragma mark - WXInvoiceItem + +@interface WXInvoiceItem : NSObject +/** 卡id + * @attention 长度不能超过1024字节 + */ +@property (nonatomic, copy) NSString *cardId; +/** ext信息 + * @attention 长度不能超过2024字节 + */ +@property (nonatomic, copy, nullable) NSString *extMsg; +/** + * @attention 卡的状态,req不需要填。resp:0为未添加,1为已添加。 + */ +@property (nonatomic, assign) UInt32 cardState; +/** + * @attention req不需要填,chooseCard返回的。 + */ +@property (nonatomic, copy) NSString *encryptCode; +/** + * @attention req不需要填,chooseCard返回的。 + */ +@property (nonatomic, copy) NSString *appID; + +@end + +#pragma mark - AddCardToWXCardPackageReq +/* ! @brief 请求添加卡券至微信卡包 + * + */ + +@interface AddCardToWXCardPackageReq : BaseReq +/** 卡列表 + * @attention 个数不能超过40个 类型WXCardItem + */ +@property (nonatomic, strong) NSArray *cardAry; + +@end + + +#pragma mark - AddCardToWXCardPackageResp +/** ! @brief 微信返回第三方添加卡券结果 + * + */ + +@interface AddCardToWXCardPackageResp : BaseResp +/** 卡列表 + * @attention 个数不能超过40个 类型WXCardItem + */ +@property (nonatomic, strong) NSArray *cardAry; +@end + +#pragma mark - WXChooseCardReq +/* ! @brief 请求从微信选取卡券 + * + */ + +@interface WXChooseCardReq : BaseReq +@property (nonatomic, copy) NSString *appID; +@property (nonatomic, assign) UInt32 shopID; +@property (nonatomic, assign) UInt32 canMultiSelect; +@property (nonatomic, copy) NSString *cardType; +@property (nonatomic, copy) NSString *cardTpID; +@property (nonatomic, copy) NSString *signType; +@property (nonatomic, copy) NSString *cardSign; +@property (nonatomic, assign) UInt32 timeStamp; +@property (nonatomic, copy) NSString *nonceStr; +@end + + +#pragma mark - WXChooseCardResp +/** ! @brief 微信返回第三方请求选择卡券结果 + * + */ + +@interface WXChooseCardResp : BaseResp +@property (nonatomic, strong ) NSArray* cardAry; +@end + + +#pragma mark - WXChooseInvoiceReq +/* ! @brief 请求从微信选取发票 + * + */ +@interface WXChooseInvoiceReq : BaseReq +@property (nonatomic, copy) NSString *appID; +@property (nonatomic, assign) UInt32 shopID; +@property (nonatomic, copy) NSString *signType; +@property (nonatomic, copy) NSString *cardSign; +@property (nonatomic, assign) UInt32 timeStamp; +@property (nonatomic, copy) NSString *nonceStr; +@end + +#pragma mark - WXChooseInvoiceResp +/** ! @brief 微信返回第三方请求选择发票结果 + * + */ +@interface WXChooseInvoiceResp : BaseResp +@property (nonatomic, strong) NSArray* cardAry; +@end + +#pragma mark - WXSubscriptionReq +@interface WXSubscribeMsgReq : BaseReq +@property (nonatomic, assign) UInt32 scene; +@property (nonatomic, copy) NSString *templateId; +@property (nonatomic, copy, nullable) NSString *reserved; +@end + +#pragma mark - WXSubscriptionReq +@interface WXSubscribeMsgResp : BaseResp + +@property (nonatomic, copy) NSString *templateId; +@property (nonatomic, assign) UInt32 scene; +@property (nonatomic, copy) NSString *action; +@property (nonatomic, copy) NSString *reserved; +@property (nonatomic, copy, nullable) NSString *openId; + +@end + +#pragma mark - WXSubscribeMiniProgramMsg +/** ! @brief 第三方请求订阅小程序消息 + * + */ +@interface WXSubscribeMiniProgramMsgReq : BaseReq +@property (nonatomic, copy) NSString *miniProgramAppid; +@end + +#pragma mark - WXSubscriptionReq +@interface WXSubscribeMiniProgramMsgResp : BaseResp + +@property(nonatomic, copy) NSString *openId; // 小程序openid +@property(nonatomic, copy) NSString *unionId; // unionId +@property(nonatomic, copy) NSString *nickName; // 用户昵称 + +@end + +#pragma mark - WXinvoiceAuthInsertReq +@interface WXInvoiceAuthInsertReq : BaseReq + +@property (nonatomic, copy) NSString *urlString; + +@end + +#pragma mark - WXinvoiceAuthInsertResp + +@interface WXInvoiceAuthInsertResp : BaseResp + +@property (nonatomic, copy) NSString *wxOrderId; + +@end + +#pragma mark - WXMediaMessage + +/*! @brief 多媒体消息结构体 + * + * 用于微信终端和第三方程序之间传递消息的多媒体消息内容 + */ +@interface WXMediaMessage : NSObject + ++ (WXMediaMessage *)message; + +/** 标题 + * @note 长度不能超过512字节 + */ +@property (nonatomic, copy) NSString *title; +/** 描述内容 + * @note 长度不能超过1K + */ +@property (nonatomic, copy) NSString *description; +/** 缩略图数据 + * @note 大小不能超过64K + */ +@property (nonatomic, strong, nullable) NSData *thumbData; +/** + * @note 长度不能超过64字节 + */ +@property (nonatomic, copy, nullable) NSString *mediaTagName; +/** + * + */ +@property (nonatomic, copy, nullable) NSString *messageExt; +@property (nonatomic, copy, nullable) NSString *messageAction; +/** + * 多媒体数据对象,可以为WXImageObject,WXMusicObject,WXVideoObject,WXWebpageObject等。 + */ +@property (nonatomic, strong) id mediaObject; + +/** 缩略图的hash值 + * @note 使用sha256得到,用于计算签名 + */ +@property (nonatomic, copy, nullable) NSString *thumbDataHash; + +/** 消息签名 + * @note 用于校验消息体是否被篡改过 + */ +@property (nonatomic, copy, nullable) NSString *msgSignature; + + +/*! @brief 设置消息缩略图的方法 + * + * @param image 缩略图 + * @note 大小不能超过256K + */ +- (void)setThumbImage:(UIImage *)image; + +@end + + + +#pragma mark - WXImageObject +/*! @brief 多媒体消息中包含的图片数据对象 + * + * 微信终端和第三方程序之间传递消息中包含的图片数据对象。 + * @note imageData成员不能为空 + * @see WXMediaMessage + */ +@interface WXImageObject : NSObject +/*! @brief 返回一个WXImageObject对象 + * + * @note 返回的WXImageObject对象是自动释放的 + */ ++ (WXImageObject *)object; + +/** 图片真实数据内容 + * @note 大小不能超过25M + */ +@property (nonatomic, strong) NSData *imageData; + +/** 图片数据的hash值 + * @note 使用sha256得到,用于计算签名 + */ +@property (nonatomic, copy, nullable) NSString *imgDataHash; + +/** 分享的图片消息是否要带小程序入口,若 'entranceMiniProgramUsername' 非空则显示 + * 仅部分小程序类目可用 + * @note 本字段为空则发送普通app图片消息 + */ +@property (nonatomic, copy, nullable) NSString *entranceMiniProgramUsername; + +/** 分享的图片消息显示的小程序入口可以跳转的小程序路径 + * 仅当 'entranceMiniProgramUsername' 非空时生效 + */ +@property (nonatomic, copy, nullable) NSString *entranceMiniProgramPath; + +@end + + +#pragma mark - WXMusicObject +/*! @brief 多媒体消息中包含的音乐数据对象 + * + * 微信终端和第三方程序之间传递消息中包含的音乐数据对象。 + * @note musicUrl和musicLowBandUrl成员不能同时为空。 + * @see WXMediaMessage + */ +@interface WXMusicObject : NSObject +/*! @brief 返回一个WXMusicObject对象 + * + * @note 返回的WXMusicObject对象是自动释放的 + */ ++ (WXMusicObject *)object; + +/** 音乐网页的url地址 + * @note 长度不能超过10K + */ +@property (nonatomic, copy) NSString *musicUrl; +/** 音乐lowband网页的url地址 + * @note 长度不能超过10K + */ +@property (nonatomic, copy) NSString *musicLowBandUrl; +/** 音乐数据url地址 + * @note 长度不能超过10K + */ +@property (nonatomic, copy) NSString *musicDataUrl; + +/**音乐lowband数据url地址 + * @note 长度不能超过10K + */ +@property (nonatomic, copy) NSString *musicLowBandDataUrl; + +/**音乐封面图Url + * @note 长度不能超过10K + */ +@property (nonatomic, copy) NSString *songAlbumUrl; + +/**歌词信息 LRC格式 + * @note 长度不能超过32K + */ +@property (nonatomic, copy, nullable) NSString *songLyric; +@end + + + +#pragma mark - WXMusicVideoObject + +@interface WXMusicVipInfo : NSObject + +/**付费歌曲的id + * @note 长度不能超过32K + */ +@property (nonatomic, copy) NSString *musicId; + +@end + + +@interface WXMusicVideoObject : NSObject + +/*! @brief 返回一个WXMusicVideoObject对象 + * + * @note 返回的WXMusicVideoObject对象是自动释放的 + */ ++ (WXMusicVideoObject *)object; + +/** 音乐网页的url地址 + * @note 长度不能超过10K,不能为空 + */ +@property (nonatomic, copy) NSString *musicUrl; + +/** 音乐数据url地址 + * @note 长度不能超过10K,不能为空 + */ +@property (nonatomic, copy) NSString *musicDataUrl; + +/**歌手名 + * @note 长度不能超过1k,不能为空 + */ +@property (nonatomic, copy) NSString *singerName; + +/** + * @note 音乐时长, 单位毫秒 + */ +@property (nonatomic, assign) UInt32 duration; + +/**歌词信息 LRC格式 + * @note 长度不能超过32K + */ +@property (nonatomic, copy) NSString *songLyric; + +/**高清封面图 + * @note 大小不能超过1M + */ +@property (nonatomic, strong) NSData *hdAlbumThumbData; + +/** 高清封面图数据的hash值 + * @note 使用sha256得到,用于计算签名 + */ +@property (nonatomic, copy, nullable) NSString *hdAlbumThumbFileHash; + +/**音乐专辑名称 + * @note 长度不能超过1k + */ +@property (nonatomic, copy, nullable) NSString *albumName; + +/**音乐流派 + * @note 长度不能超过1k + */ +@property (nonatomic, copy, nullable) NSString *musicGenre; + +/**发行时间 + * @note Unix时间戳,单位为秒 + */ +@property (nonatomic, assign) UInt64 issueDate; + +/**音乐标识符 + * @note 长度不能超过1K,从微信跳回应用时会带上 + */ +@property (nonatomic, copy, nullable) NSString *identification; + +/**运营H5地址 + * @note 选填,建议填写,用户进入歌曲详情页将展示内嵌的运营H5,可展示该歌曲的相关评论、歌曲推荐等内容,不可诱导下载、分享等。 + */ +@property (nonatomic, copy, nullable) NSString *musicOperationUrl; + +/** 付费歌曲相关信息 + * @note 选填,如果歌曲是需要付费的,那么将付费歌曲id等信息封装在内。 + */ +@property (nonatomic, strong) WXMusicVipInfo *musicVipInfo; + +@end + + + +#pragma mark - WXVideoObject +/*! @brief 多媒体消息中包含的视频数据对象 + * + * 微信终端和第三方程序之间传递消息中包含的视频数据对象。 + * @note videoUrl和videoLowBandUrl不能同时为空。 + * @see WXMediaMessage + */ +@interface WXVideoObject : NSObject +/*! @brief 返回一个WXVideoObject对象 + * + * @note 返回的WXVideoObject对象是自动释放的 + */ ++ (WXVideoObject *)object; + +/** 视频网页的url地址 + * @note 长度不能超过10K + */ +@property (nonatomic, copy) NSString *videoUrl; +/** 视频lowband网页的url地址 + * @note 长度不能超过10K + */ +@property (nonatomic, copy) NSString *videoLowBandUrl; + +@end + + + +#pragma mark - WXWebpageObject +/*! @brief 多媒体消息中包含的网页数据对象 + * + * 微信终端和第三方程序之间传递消息中包含的网页数据对象。 + * @see WXMediaMessage + */ +@interface WXWebpageObject : NSObject +/*! @brief 返回一个WXWebpageObject对象 + * + * @note 返回的WXWebpageObject对象是自动释放的 + */ ++ (WXWebpageObject *)object; + +/** 网页的url地址 + * @note 不能为空且长度不能超过10K + */ +@property (nonatomic, copy) NSString *webpageUrl; + +/**是否是私密消息 + */ +@property (nonatomic, assign) BOOL isSecretMessage; + +/** 业务所需的额外信息 */ +@property (nonatomic, strong, nullable) NSDictionary *extraInfoDic; + +@end + + + +#pragma mark - WXAppExtendObject +/*! @brief 多媒体消息中包含的App扩展数据对象 + * + * 第三方程序向微信终端发送包含WXAppExtendObject的多媒体消息, + * 微信需要处理该消息时,会调用该第三方程序来处理多媒体消息内容。 + * @note url,extInfo和fileData不能同时为空 + * @see WXMediaMessage + */ +@interface WXAppExtendObject : NSObject +/*! @brief 返回一个WXAppExtendObject对象 + * + * @note 返回的WXAppExtendObject对象是自动释放的 + */ ++ (WXAppExtendObject *)object; + +/** 若第三方程序不存在,微信终端会打开该url所指的App下载地址 + * @note 长度不能超过10K + */ +@property (nonatomic, copy) NSString *url; +/** 第三方程序自定义简单数据,微信终端会回传给第三方程序处理 + * @note 长度不能超过2K + */ +@property (nonatomic, copy, nullable) NSString *extInfo; +/** App文件数据,该数据发送给微信好友,微信好友需要点击后下载数据,微信终端会回传给第三方程序处理 + * @note 大小不能超过10M + */ +@property (nonatomic, strong, nullable) NSData *fileData; + +@end + + + +#pragma mark - WXEmoticonObject +/*! @brief 多媒体消息中包含的表情数据对象 + * + * 微信终端和第三方程序之间传递消息中包含的表情数据对象。 + * @see WXMediaMessage + */ +@interface WXEmoticonObject : NSObject + +/*! @brief 返回一个WXEmoticonObject对象 + * + * @note 返回的WXEmoticonObject对象是自动释放的 + */ ++ (WXEmoticonObject *)object; + +/** 表情真实数据内容 + * @note 大小不能超过10M + */ +@property (nonatomic, strong) NSData *emoticonData; + +@end + + + +#pragma mark - WXFileObject +/*! @brief 多媒体消息中包含的文件数据对象 + * + * @see WXMediaMessage + */ +@interface WXFileObject : NSObject + +/*! @brief 返回一个WXFileObject对象 + * + * @note 返回的WXFileObject对象是自动释放的 + */ ++ (WXFileObject *)object; + +/** 文件后缀名 + * @note 长度不超过64字节 + */ +@property (nonatomic, copy) NSString *fileExtension; + +/** 文件真实数据内容 + * @note 大小不能超过10M + */ +@property (nonatomic, strong) NSData *fileData; + +@end + + +#pragma mark - WXLocationObject +/*! @brief 多媒体消息中包含的地理位置数据对象 + * + * 微信终端和第三方程序之间传递消息中包含的地理位置数据对象。 + * @see WXMediaMessage + */ +@interface WXLocationObject : NSObject + +/*! @brief 返回一个WXLocationObject对象 + * + * @note 返回的WXLocationObject对象是自动释放的 + */ ++ (WXLocationObject *)object; + +/** 地理位置信息 + * @note 经纬度 + */ +@property (nonatomic, assign) double lng; //经度 +@property (nonatomic, assign) double lat; //纬度 + +@end + +#pragma mark - WXTextObject +/*! @brief 多媒体消息中包含的文本数据对象 + * + * 微信终端和第三方程序之间传递消息中包含的文本数据对象。 + * @see WXMediaMessage + */ +@interface WXTextObject : NSObject + +/*! @brief 返回一个WXTextObject对象 + * + * @note 返回的WXTextObject对象是自动释放的 + */ ++ (WXTextObject *)object; + +/** 地理位置信息 + * @note 文本内容 + */ +@property (nonatomic, copy) NSString *contentText; + +@end + +#pragma mark - WXMiniProgramObject + +@interface WXMiniProgramObject : NSObject + +/*! @brief WXMiniProgramObject对象 + * + * @note 返回的WXMiniProgramObject对象是自动释放的 + */ ++ (WXMiniProgramObject *)object; + +/** 低版本网页链接 + * @attention 长度不能超过1024字节 + */ +@property (nonatomic, copy) NSString *webpageUrl; + +/** 小程序username */ +@property (nonatomic, copy) NSString *userName; + +/** 小程序页面的路径 + * @attention 不填默认拉起小程序首页 + */ +@property (nonatomic, copy, nullable) NSString *path; + +/** 小程序新版本的预览图 + * @attention 大小不能超过128k + */ +@property (nonatomic, strong, nullable) NSData *hdImageData; + +/** 是否使用带 shareTicket 的转发 */ +@property (nonatomic, assign) BOOL withShareTicket; + +/** 分享小程序的版本 + * @attention (正式,开发,体验) + */ +@property (nonatomic, assign) WXMiniProgramType miniProgramType; + +/** 是否禁用转发 */ +@property (nonatomic, assign) BOOL disableForward; + +@property (nonatomic, assign) BOOL isUpdatableMessage; + +@property (nonatomic, assign) BOOL isSecretMessage; + + +/** 业务所需的额外信息 */ +@property (nonatomic, strong, nullable) NSDictionary *extraInfoDic; + +@end + +#pragma mark - WXGameLiveObject + +/*! @brief WXGameLiveObject对象 + * + * @note 游戏直播消息类型 + */ + +@interface WXGameLiveObject : NSObject + ++ (WXGameLiveObject *)object; + +/** 业务所需的额外信息 */ +@property (nonatomic, strong, nullable) NSDictionary *extraInfoDic; + +@end + +@interface WXNativeGamePageObject : NSObject + +/** 是否为视频类型 + */ +@property (nonatomic, assign) BOOL isVideo; + +/** 视频时长 + @note 当为视频类型时,必填;单位为秒 + */ +@property (nonatomic, assign) UInt32 videoDuration; + +/** 透传字段 + @note 长度限制为100K + */ +@property (nonatomic, copy) NSString *shareData; + +/** 缩略图 + @note 大小限制为256K + */ +@property (nonatomic, strong) NSData *gameThumbData; + ++ (WXNativeGamePageObject *)object; + +@end + +#pragma mark - WXLaunchMiniProgramReq + +/*! @brief WXLaunchMiniProgramReq对象, 可实现通过sdk拉起微信小程序 + * + * @note 返回的WXLaunchMiniProgramReq对象是自动释放的 + */ +@interface WXLaunchMiniProgramReq : BaseReq + ++ (WXLaunchMiniProgramReq *)object; + +/** 小程序username */ +@property (nonatomic, copy) NSString *userName; + +/** 小程序页面的路径 + * @attention 不填默认拉起小程序首页 + */ +@property (nonatomic, copy, nullable) NSString *path; + +/** 分享小程序的版本 + * @attention (正式,开发,体验) + */ +@property (nonatomic, assign) WXMiniProgramType miniProgramType; + +/** ext信息 + * @attention json格式 + */ +@property (nonatomic, copy, nullable) NSString *extMsg; + +/** extDic + * @attention 字典,可存放图片等比较大的数据 + */ +@property (nonatomic, copy, nullable) NSDictionary *extDic; + +@end + +#pragma mark - WXLaunchMiniProgramResp +/*! @brief 微信终端向第三方程序返回的WXLaunchMiniProgramReq处理结果。 + * + * 第三方程序向微信终端发送WXLaunchMiniProgramReq后,微信发送回来的处理结果,该结果用WXLaunchMiniProgramResp表示。 + */ +@interface WXLaunchMiniProgramResp : BaseResp + +@property (nonatomic, copy, nullable) NSString *extMsg; + +@end + + +#pragma mark - WXOpenBusinessViewReq +/*! @brief WXOpenBusinessViewReq对象, 可实现第三方通知微信启动,打开业务页面 + * + * @note 返回的WXOpenBusinessViewReq对象是自动释放的 + */ + +@interface WXOpenBusinessViewReq : BaseReq + ++ (WXOpenBusinessViewReq *)object; + +/** 业务类型 + */ +@property (nonatomic, copy) NSString *businessType; + +/** 业务参数 + */ +@property (nonatomic, copy, nullable) NSString *query; + +/** ext信息 + * @note 选填,json格式 + */ +@property (nonatomic, copy, nullable) NSString *extInfo; + +/** extData数据 + * @note + */ +@property (nonatomic, strong, nullable) NSData *extData; +@end + + +@interface WXOpenBusinessViewResp : BaseResp + +/** 业务类型 + */ +@property (nonatomic, copy) NSString *businessType; + +/** 业务返回数据 + */ +@property (nonatomic, copy, nullable) NSString *extMsg; + +@end + +#pragma mark - WXOpenCustomerServiceReq +@interface WXOpenCustomerServiceReq : BaseReq + ++ (WXOpenCustomerServiceReq *)object; + +/**企微客服发起流程 url + */ +@property (nonatomic, copy, nullable) NSString *url; + +/**企业 id + */ +@property (nonatomic, copy, nullable) NSString *corpid; + +@end + +@interface WXOpenCustomerServiceResp : BaseResp + +/** 业务返回数据 + */ +@property (nonatomic, copy, nullable) NSString *extMsg; + +@end + + +#pragma mark - WXChannelStartLiveReq + +@interface WXChannelStartLiveReq : BaseReq + ++ (WXChannelStartLiveReq *)object; + +/** 必填,直播业务数据(json格式) + */ +@property (nonatomic, copy) NSString *liveJsonInfo; + +@end + +@interface WXChannelStartLiveResp : BaseResp + +/** 业务返回数据 + */ +@property (nonatomic, copy, nullable) NSString *extMsg; + +@end + +NS_ASSUME_NONNULL_END diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Headers/WechatAuthSDK.h b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Headers/WechatAuthSDK.h new file mode 100644 index 0000000..308aaea --- /dev/null +++ b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Headers/WechatAuthSDK.h @@ -0,0 +1,68 @@ +// +// WechatAuthSDK.h +// WechatAuthSDK +// +// Created by 李凯 on 13-11-29. +// Copyright (c) 2013年 Tencent. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +enum AuthErrCode { + WechatAuth_Err_Ok = 0, //Auth成功 + WechatAuth_Err_NormalErr = -1, //普通错误 + WechatAuth_Err_NetworkErr = -2, //网络错误 + WechatAuth_Err_GetQrcodeFailed = -3, //获取二维码失败 + WechatAuth_Err_Cancel = -4, //用户取消授权 + WechatAuth_Err_Timeout = -5, //超时 +}; + +@protocol WechatAuthAPIDelegate +@optional + +- (void)onAuthGotQrcode:(UIImage *)image; //得到二维码 +- (void)onQrcodeScanned; //二维码被扫描 +- (void)onAuthFinish:(int)errCode AuthCode:(nullable NSString *)authCode; //成功登录 + +@end + +@interface WechatAuthSDK : NSObject{ + NSString *_sdkVersion; + __weak id _delegate; +} + +@property(nonatomic, weak, nullable) id delegate; +@property(nonatomic, readonly) NSString *sdkVersion; //authSDK版本号 + +/*! @brief 发送登录请求,等待WechatAuthAPIDelegate回调 + * + * @param appId 微信开发者ID + * @param nonceStr 一个随机的尽量不重复的字符串,用来使得每次的signature不同 + * @param timeStamp 时间戳 + * @param scope 应用授权作用域,拥有多个作用域用逗号(,)分隔 + * @param signature 签名 + * @param schemeData 会在扫码后拼在scheme后 + * @return 成功返回YES,失败返回NO + 注:该实现只保证同时只有一个Auth在运行,Auth未完成或未Stop再次调用Auth接口时会返回NO。 + */ + +- (BOOL)Auth:(NSString *)appId + nonceStr:(NSString *)nonceStr + timeStamp:(NSString *)timeStamp + scope:(NSString *)scope + signature:(NSString *)signature + schemeData:(nullable NSString *)schemeData; + + +/*! @brief 暂停登录请求 + * + * @return 成功返回YES,失败返回NO。 + */ +- (BOOL)StopAuth; + +@end + +NS_ASSUME_NONNULL_END diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Headers/WechatOpenSDK.h b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Headers/WechatOpenSDK.h new file mode 100644 index 0000000..979e5e9 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Headers/WechatOpenSDK.h @@ -0,0 +1,9 @@ +// WechatOpenSDK.h +// +// Created by Wechat. +// Copyright (c) 2012年 Tencent. All rights reserved. +// +#import +#import +#import + diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Info.plist b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Info.plist new file mode 100755 index 0000000..45d6068 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Info.plist @@ -0,0 +1,15 @@ + + + + CFBundleIdentifier + com.tencent.WechatOpenSDK + CFBundleVersion + 1.0.0 + CFBundleShortVersionString + 1.0 + MinimumOSVersion + 12.0 + CFBundleExecutable + WechatOpenSDK + + \ No newline at end of file diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Modules/module.modulemap b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Modules/module.modulemap new file mode 100644 index 0000000..3ddbeb0 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module WechatOpenSDK { + umbrella header "WechatOpenSDK.h" + export * + link "WechatOpenSDK" +} diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/WechatOpenSDK b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/WechatOpenSDK new file mode 100644 index 0000000..2a32d92 Binary files /dev/null and b/XinjiangProject/XinjiangProject/Manager/WXManager/WechatOpenSDK-NoPay.xcframework/ios-arm64_x86_64-simulator/WechatOpenSDK.framework/WechatOpenSDK differ diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/XJWeChatManager.h b/XinjiangProject/XinjiangProject/Manager/WXManager/XJWeChatManager.h new file mode 100644 index 0000000..8f12807 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Manager/WXManager/XJWeChatManager.h @@ -0,0 +1,81 @@ +// +// XJWeChatManager.h +// XinjiangProject +// +// 微信分享管理类(手动集成 WechatOpenSDK-NoPay) +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/// 微信分享结果回调 +typedef void(^XJWeChatShareCompletion)(BOOL success, NSString *_Nullable errorMsg); + +@interface XJWeChatManager : NSObject + ++ (instancetype)sharedManager; + +#pragma mark - 注册 +/// 注册微信 SDK(需在 AppDelegate didFinishLaunching 中调用) +/// @param appID 微信开放平台 AppID +/// @param universalLink 通用链接 +- (BOOL)registerWithAppID:(NSString *)appID universalLink:(NSString *)universalLink; + +/// 处理微信 URL 回调(AppDelegate handleOpenURL 中调用) +- (BOOL)handleOpenURL:(NSURL *)url; + +/// 处理微信 Universal Link 回调(AppDelegate continueUserActivity 中调用) +- (BOOL)handleOpenUniversalLink:(NSUserActivity *)userActivity; + +#pragma mark - 分享 +/// 分享文字 +- (void)shareText:(NSString *)text + toScene:(int)scene + completion:(XJWeChatShareCompletion _Nullable)completion; + +/// 分享图片(NSData) +- (void)shareImageData:(NSData *)imageData + toScene:(int)scene + completion:(XJWeChatShareCompletion _Nullable)completion; + +/// 分享网页 +/// @param title 标题 +/// @param desc 描述 +/// @param webpageUrl 网页链接 +/// @param thumbImage 缩略图(不超过 32KB) +/// @param scene 分享目标:WXSceneSession=好友, WXSceneTimeline=朋友圈 +- (void)shareWebpageWithTitle:(NSString *)title + desc:(NSString *)desc + webpageUrl:(NSString *)webpageUrl + thumbImage:(UIImage *_Nullable)thumbImage + toScene:(int)scene + completion:(XJWeChatShareCompletion _Nullable)completion; + +/// 分享小程序卡片 +/// @param title 标题 +/// @param desc 描述 +/// @param webpageUrl 兼容低版本网页链接 +/// @param userName 小程序原始ID(gh_ 开头) +/// @param path 小程序页面路径 +/// @param miniProgramType 小程序类型:0=正式版, 1=开发版, 2=体验版 +- (void)shareMiniProgramWithTitle:(NSString *)title + desc:(NSString *)desc + webpageUrl:(NSString *)webpageUrl + userName:(NSString *)userName + path:(NSString *)path + miniProgramType:(NSUInteger)miniProgramType + thumbImage:(UIImage *_Nullable)thumbImage + completion:(XJWeChatShareCompletion _Nullable)completion; + +#pragma mark - 工具 +/// 是否安装微信 +- (BOOL)isWeChatInstalled; + +/// 微信是否支持 OpenAPI +- (BOOL)isWeChatSupportApi; + +@end + +NS_ASSUME_NONNULL_END diff --git a/XinjiangProject/XinjiangProject/Manager/WXManager/XJWeChatManager.m b/XinjiangProject/XinjiangProject/Manager/WXManager/XJWeChatManager.m new file mode 100644 index 0000000..61a67ee --- /dev/null +++ b/XinjiangProject/XinjiangProject/Manager/WXManager/XJWeChatManager.m @@ -0,0 +1,160 @@ +// +// XJWeChatManager.m +// XinjiangProject +// +// 微信分享管理类 +// + +#import "XJWeChatManager.h" +#import + +@interface XJWeChatManager () +@property (nonatomic, copy) XJWeChatShareCompletion shareCompletion; +@end + +@implementation XJWeChatManager + +#pragma mark - ————— 单例 ————— ++ (instancetype)sharedManager { + static XJWeChatManager *instance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [[XJWeChatManager alloc] init]; + }); + return instance; +} + +#pragma mark - ————— 注册 ————— +- (BOOL)registerWithAppID:(NSString *)appID universalLink:(NSString *)universalLink { + return [WXApi registerApp:appID universalLink:universalLink]; +} + +- (BOOL)handleOpenURL:(NSURL *)url { + return [WXApi handleOpenURL:url delegate:self]; +} + +- (BOOL)handleOpenUniversalLink:(NSUserActivity *)userActivity { + return [WXApi handleOpenUniversalLink:userActivity delegate:self]; +} + +#pragma mark - ————— 分享 ————— +- (void)shareText:(NSString *)text + toScene:(int)scene + completion:(XJWeChatShareCompletion)completion { + self.shareCompletion = completion; + + SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; + req.bText = YES; + req.text = text; + req.scene = scene; + + [WXApi sendReq:req completion:nil]; +} + +- (void)shareImageData:(NSData *)imageData + toScene:(int)scene + completion:(XJWeChatShareCompletion)completion { + self.shareCompletion = completion; + + WXMediaMessage *message = [WXMediaMessage message]; + message.thumbData = nil; + + WXImageObject *imageObject = [WXImageObject object]; + imageObject.imageData = imageData; + message.mediaObject = imageObject; + + SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; + req.bText = NO; + req.message = message; + req.scene = scene; + + [WXApi sendReq:req completion:nil]; +} + +- (void)shareWebpageWithTitle:(NSString *)title + desc:(NSString *)desc + webpageUrl:(NSString *)webpageUrl + thumbImage:(UIImage *)thumbImage + toScene:(int)scene + completion:(XJWeChatShareCompletion)completion { + self.shareCompletion = completion; + + WXMediaMessage *message = [WXMediaMessage message]; + message.title = title; + message.description = desc; + + if (thumbImage) { + message.thumbData = UIImageJPEGRepresentation(thumbImage, 0.5); + } + + WXWebpageObject *webpageObject = [WXWebpageObject object]; + webpageObject.webpageUrl = webpageUrl; + message.mediaObject = webpageObject; + + SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; + req.bText = NO; + req.message = message; + req.scene = scene; + + [WXApi sendReq:req completion:nil]; +} + +- (void)shareMiniProgramWithTitle:(NSString *)title + desc:(NSString *)desc + webpageUrl:(NSString *)webpageUrl + userName:(NSString *)userName + path:(NSString *)path + miniProgramType:(NSUInteger)miniProgramType + thumbImage:(UIImage *)thumbImage + completion:(XJWeChatShareCompletion)completion { + self.shareCompletion = completion; + + WXMediaMessage *message = [WXMediaMessage message]; + message.title = title; + message.description = desc; + + if (thumbImage) { + message.thumbData = UIImageJPEGRepresentation(thumbImage, 0.5); + } + + WXMiniProgramObject *miniProgramObject = [WXMiniProgramObject object]; + miniProgramObject.webpageUrl = webpageUrl; + miniProgramObject.userName = userName; + miniProgramObject.path = path; + miniProgramObject.miniProgramType = miniProgramType; + message.mediaObject = miniProgramObject; + + SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; + req.bText = NO; + req.message = message; + req.scene = WXSceneSession; + + [WXApi sendReq:req completion:nil]; +} + +#pragma mark - ————— 工具 ————— +- (BOOL)isWeChatInstalled { + return [WXApi isWXAppInstalled]; +} + +- (BOOL)isWeChatSupportApi { + return [WXApi isWXAppSupportApi]; +} + +#pragma mark - ————— WXApiDelegate ————— +- (void)onResp:(BaseResp *)resp { + if ([resp isKindOfClass:[SendMessageToWXResp class]]) { + if (self.shareCompletion) { + if (resp.errCode == WXSuccess) { + self.shareCompletion(YES, nil); + } else if (resp.errCode == WXErrCodeUserCancel) { + self.shareCompletion(NO, @"用户取消分享"); + } else { + self.shareCompletion(NO, resp.errStr ?: @"分享失败"); + } + self.shareCompletion = nil; + } + } +} + +@end diff --git a/XinjiangProject/XinjiangProject/Project/AppDelegate/AppDelegate+AppService.m b/XinjiangProject/XinjiangProject/Project/AppDelegate/AppDelegate+AppService.m index 2229a96..65c938d 100644 --- a/XinjiangProject/XinjiangProject/Project/AppDelegate/AppDelegate+AppService.m +++ b/XinjiangProject/XinjiangProject/Project/AppDelegate/AppDelegate+AppService.m @@ -55,7 +55,7 @@ if ([HQCommonUtils isLogin]) { //腾讯im #if DEBUG -// [self loginImAccount]; + [self loginImAccount]; #else [self loginImAccount]; diff --git a/XinjiangProject/XinjiangProject/Project/AppDelegate/AppDelegate.m b/XinjiangProject/XinjiangProject/Project/AppDelegate/AppDelegate.m index e4df276..4223a13 100644 --- a/XinjiangProject/XinjiangProject/Project/AppDelegate/AppDelegate.m +++ b/XinjiangProject/XinjiangProject/Project/AppDelegate/AppDelegate.m @@ -7,6 +7,7 @@ #import "AppDelegate.h" #import "SJRotationManager.h" +#import "XJWeChatManager.h" #import @implementation AppDelegate @@ -34,8 +35,10 @@ SDImageWebPCoder*webPCoder = [SDImageWebPCoder sharedCoder]; [[SDImageCodersManager sharedManager] addCoder:webPCoder]; + // 注册微信 SDK + [[XJWeChatManager sharedManager] registerWithAppID:kAppKey_Wechat + universalLink:kUniversalLink_Wechat]; - return YES; } @@ -56,6 +59,17 @@ } - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window { + if (window == self.window) { + // 遍历找到最顶层的 presented VC + UIViewController *vc = self.window.rootViewController; + while (vc.presentedViewController) { vc = vc.presentedViewController; } + // WKWebView 视频全屏(WKFullScreenViewController)或 AVPlayer 全屏时放行横屏 + NSString *className = NSStringFromClass(vc.class); + if ([className containsString:@"FullScreen"] || [className containsString:@"AVPlayer"]) { + return UIInterfaceOrientationMaskAllButUpsideDown; + } + return UIInterfaceOrientationMaskPortrait; + } return [SJRotationManager supportedInterfaceOrientationsForWindow:window]; } @@ -74,10 +88,15 @@ } +- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { + return [[XJWeChatManager sharedManager] handleOpenURL:url]; +} + - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray> * __nullable restorableObjects))restorationHandler { - + if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) { + return [[XJWeChatManager sharedManager] handleOpenUniversalLink:userActivity]; + } return YES; - } diff --git a/XinjiangProject/XinjiangProject/Project/Base/XJBaseWebViewVC.m b/XinjiangProject/XinjiangProject/Project/Base/XJBaseWebViewVC.m index 56d11f8..9bb5670 100644 --- a/XinjiangProject/XinjiangProject/Project/Base/XJBaseWebViewVC.m +++ b/XinjiangProject/XinjiangProject/Project/Base/XJBaseWebViewVC.m @@ -14,6 +14,10 @@ //@property (nonatomic, copy)NSString *videoUrl; //@property (nonatomic, copy)NSString *messageType; +/// 网页加载进度条 +@property (nonatomic, strong) UIProgressView *progressView; +/// 加载失败占位视图 +@property (nonatomic, strong) UIView *errorView; @end @@ -42,7 +46,6 @@ @implementation XJBaseWebViewVC - #pragma mark - LifeCycle - (void)viewDidLoad { [super viewDidLoad]; @@ -61,6 +64,8 @@ } - (void)dealloc { + // 移除进度条 KVO + [self.wkWebView removeObserver:self forKeyPath:@"estimatedProgress"]; // 移除所有 scriptMessageHandler,配合 XJWeakScriptMessageHandler 彻底避免内存泄漏 WKUserContentController *ucc = self.wkWebView.configuration.userContentController; [ucc removeScriptMessageHandlerForName:@"JSCallback"]; @@ -101,7 +106,8 @@ preferences.javaScriptCanOpenWindowsAutomatically = YES; preferences.minimumFontSize = 10; configuration.preferences = preferences; - + // 允许视频内联播放,避免全屏时布局计算错误导致显示不完整 + configuration.allowsInlineMediaPlayback = YES; // ── 统一使用 configuration 自带的 userContentController ────────────────── // Bug 修复:原先在下方又新建了 wkUController 并覆盖 configuration.userContentController, // 导致此处注册的 JSCallback / htmlBack 被丢弃,永远收不到 JS 消息。 @@ -131,8 +137,27 @@ // configuration.userContentController = wkUController; [configuration.userContentController addUserScript:wkUScript]; + + // 禁用网页长按文字选中和复制粘贴菜单 + NSString *noSelectJS = @"document.documentElement.style.webkitUserSelect='none';" + "document.documentElement.style.webkitTouchCallout='none';"; + WKUserScript *noSelectScript = [[WKUserScript alloc] initWithSource:noSelectJS + injectionTime:WKUserScriptInjectionTimeAtDocumentEnd + forMainFrameOnly:NO]; + [configuration.userContentController addUserScript:noSelectScript]; self.wkWebView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, WIDTH, MyViewHEIGHT) configuration:configuration]; + if (@available(iOS 16.4, *)) { + self.wkWebView.inspectable = YES; + } + // 伪装成 Safari User-Agent,排查 H5 API 请求因 UA 差异被拒的问题 + { + NSString *osVer = [UIDevice currentDevice].systemVersion; + NSString *osVerUnder = [osVer stringByReplacingOccurrencesOfString:@"." withString:@"_"]; + self.wkWebView.customUserAgent = [NSString stringWithFormat: + @"Mozilla/5.0 (iPhone; CPU iPhone OS %@ like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/%@ Mobile/15E148 Safari/604.1", + osVerUnder, osVer]; + } self.wkWebView.navigationDelegate = self; self.wkWebView.UIDelegate = self; if (@available(iOS 11.0, *)) { @@ -140,17 +165,78 @@ } [self.view addSubview:self.wkWebView]; self.wkWebView.scrollView.bounces = false;//禁止滑动 - [self webViewRequest]; - /* - [self.wkWebView evaluateJavaScript:@"htmlBack" completionHandler:^(id _Nullable response, NSError * _Nullable error) { - NSLog(@"response: %@ error: %@", response, error); - if (response) { - [self back]; - } - }]; - */ - + // 进度条,居中偏上 + self.progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; + self.progressView.trackTintColor = [UIColor clearColor]; + [self.view addSubview:self.progressView]; + [self.progressView mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerX.equalTo(self.wkWebView); + make.centerY.equalTo(self.wkWebView).offset(-60); + make.width.mas_offset(200); + make.height.mas_offset(2); + }]; + + // KVO 监听加载进度 + [self.wkWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil]; + + // 加载失败占位视图(默认隐藏) + self.errorView = [[UIView alloc] init]; + self.errorView.backgroundColor = [UIColor whiteColor]; + self.errorView.hidden = YES; + [self.view addSubview:self.errorView]; + [self.errorView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.equalTo(self.wkWebView); + }]; + + UILabel *tipLabel = [[UILabel alloc] init]; + tipLabel.text = @"网络加载失败\n请检查网络后重试"; + tipLabel.textAlignment = NSTextAlignmentCenter; + tipLabel.numberOfLines = 0; + tipLabel.textColor = [UIColor colorWithRed:0.6 green:0.6 blue:0.6 alpha:1.0]; + tipLabel.font = [UIFont systemFontOfSize:15]; + [self.errorView addSubview:tipLabel]; + [tipLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerX.equalTo(self.errorView); + make.centerY.equalTo(self.errorView).offset(-40); + make.left.greaterThanOrEqualTo(self.errorView).offset(20); + make.right.lessThanOrEqualTo(self.errorView).offset(-20); + }]; + + UIButton *reloadBtn = [UIButton buttonWithType:UIButtonTypeCustom]; + [reloadBtn setTitle:@"重新加载" forState:UIControlStateNormal]; + [reloadBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + reloadBtn.titleLabel.font = [UIFont systemFontOfSize:15]; + reloadBtn.backgroundColor = [UIColor colorWithRed:0.0 green:0.68 blue:0.41 alpha:1.0]; + reloadBtn.layer.cornerRadius = 20; + reloadBtn.layer.masksToBounds = YES; + [reloadBtn addTarget:self action:@selector(reloadWebView) forControlEvents:UIControlEventTouchUpInside]; + [self.errorView addSubview:reloadBtn]; + [reloadBtn mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(tipLabel.mas_bottom).offset(24); + make.right.equalTo(self.errorView.mas_centerX).offset(-8); + make.width.mas_offset(110); + make.height.mas_offset(40); + }]; + + UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom]; + [backBtn setTitle:@"返回" forState:UIControlStateNormal]; + [backBtn setTitleColor:[UIColor colorWithRed:0.0 green:0.68 blue:0.41 alpha:1.0] forState:UIControlStateNormal]; + backBtn.titleLabel.font = [UIFont systemFontOfSize:15]; + backBtn.backgroundColor = [UIColor whiteColor]; + backBtn.layer.cornerRadius = 20; + backBtn.layer.masksToBounds = YES; + backBtn.layer.borderWidth = 1; + backBtn.layer.borderColor = [UIColor colorWithRed:0.0 green:0.68 blue:0.41 alpha:1.0].CGColor; + [backBtn addTarget:self action:@selector(backVC) forControlEvents:UIControlEventTouchUpInside]; + [self.errorView addSubview:backBtn]; + [backBtn mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.equalTo(reloadBtn); + make.left.equalTo(self.errorView.mas_centerX).offset(8); + make.width.height.equalTo(reloadBtn); + }]; + + [self webViewRequest]; } - (void)finishActivity { @@ -161,12 +247,22 @@ } +- (void)reloadWebView { + self.errorView.hidden = YES; + [self webViewRequest]; +} + - (void)webViewRequest{ + [self clearWebViewCache]; NSString *urlString = self.webUrl; + DLog(@"[WebView] 请求加载: %@", urlString); [self hiddenAllMBProgressHUD]; if (urlString.length > 0) { [self showMBProgressHUDOnlyWithString:@"加载中"]; NSURL *url = [NSURL URLWithString:urlString]; + if (!url) { + DLog(@"[WebView] URL 无效: %@", urlString); + } NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url]; if (self.wkWebView) { [self.wkWebView loadRequest:urlRequest]; @@ -177,6 +273,24 @@ +#pragma mark - KVO + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + if ([keyPath isEqualToString:@"estimatedProgress"]) { + float progress = [change[NSKeyValueChangeNewKey] floatValue]; + self.progressView.alpha = 1.0; + [self.progressView setProgress:progress animated:YES]; + if (progress >= 1.0) { + // 加载完成后渐隐进度条 + [UIView animateWithDuration:0.3 delay:0.2 options:0 animations:^{ + self.progressView.alpha = 0; + } completion:^(BOOL finished) { + [self.progressView setProgress:0 animated:NO]; + }]; + } + } +} + #pragma mark - WKNavigationDelegate - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ if ([message.name isEqualToString:@"JSCallback"]) { @@ -220,14 +334,17 @@ } - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{ + DLog(@"[WebView] 开始加载: %@", webView.URL); + self.errorView.hidden = YES; if (!self.webTitle) { self.title = @"加载中..."; } } - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{ + DLog(@"[WebView] 加载完成: %@", webView.URL); [self hiddenAllMBProgressHUD]; - + self.errorView.hidden = YES; if (!self.webTitle) { self.title = webView.title; } @@ -235,7 +352,23 @@ } - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(nonnull NSError *)error{ + NSLog(@"[WebView] 加载失败 URL=%@ errorCode=%ld domain=%@ desc=%@", + webView.URL, (long)error.code, error.domain, error.localizedDescription); [self hiddenAllMBProgressHUD]; + // NSURLErrorCancelled (-999) 是主动取消导航(如跳转第三方 App),不算真实网络错误 + if (error.code != NSURLErrorCancelled) { + self.errorView.hidden = NO; + } +} + +- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error { + NSLog(@"[WebView] 导航失败 URL=%@ errorCode=%ld domain=%@ desc=%@", + webView.URL, (long)error.code, error.domain, error.localizedDescription); + [self hiddenAllMBProgressHUD]; + // NSURLErrorCancelled (-999) 是主动取消导航,不算真实网络错误 + if (error.code != NSURLErrorCancelled) { + self.errorView.hidden = NO; + } } #pragma mark - 白屏修复 diff --git a/XinjiangProject/XinjiangProject/Project/CustomTUIChat/CustomModel/CustomTUIHistoryModel.h b/XinjiangProject/XinjiangProject/Project/CustomTUIChat/CustomModel/CustomTUIHistoryModel.h index e362a1f..5bf4183 100644 --- a/XinjiangProject/XinjiangProject/Project/CustomTUIChat/CustomModel/CustomTUIHistoryModel.h +++ b/XinjiangProject/XinjiangProject/Project/CustomTUIChat/CustomModel/CustomTUIHistoryModel.h @@ -39,6 +39,7 @@ NS_ASSUME_NONNULL_BEGIN //地图 @property (nonatomic, assign) CGFloat Longitude; @property (nonatomic, assign) CGFloat Latitude; +@property (nonatomic, copy) NSString * address;//地址描述 @end diff --git a/XinjiangProject/XinjiangProject/Project/CustomTUIChat/cell/CustomMapMessageCell.h b/XinjiangProject/XinjiangProject/Project/CustomTUIChat/cell/CustomMapMessageCell.h index bea9980..1ece2ba 100644 --- a/XinjiangProject/XinjiangProject/Project/CustomTUIChat/cell/CustomMapMessageCell.h +++ b/XinjiangProject/XinjiangProject/Project/CustomTUIChat/cell/CustomMapMessageCell.h @@ -6,14 +6,12 @@ // #import -#import #import "CustomMapMessageCellData.h" #import "CustomTUIHistoryModel.h" -@interface CustomMapMessageCell : UITableViewCell +@interface CustomMapMessageCell : TUIBubbleMessageCell @property (nonatomic, strong) CustomTUIHistoryModel * model; - @end diff --git a/XinjiangProject/XinjiangProject/Project/CustomTUIChat/cell/CustomMapMessageCell.m b/XinjiangProject/XinjiangProject/Project/CustomTUIChat/cell/CustomMapMessageCell.m index 87e8388..68a168f 100644 --- a/XinjiangProject/XinjiangProject/Project/CustomTUIChat/cell/CustomMapMessageCell.m +++ b/XinjiangProject/XinjiangProject/Project/CustomTUIChat/cell/CustomMapMessageCell.m @@ -6,224 +6,161 @@ // #import "CustomMapMessageCell.h" +#import "XJWeChatManager.h" +#import #import @interface CustomMapMessageCell () -@property (nonatomic, strong) UIImageView * heardImage; - -@property (nonatomic, strong) UILabel * nickNameLabel; - -@property (nonatomic, strong) UILabel * myTextLabel; // 展示文本 - -@property (nonatomic, strong) UIImageView * mapImage; // - -@property (nonatomic, strong) UIView * backView; +@property (nonatomic, strong) UILabel * myTextLabel; +@property (nonatomic, strong) UIImageView * mapImage; @end @implementation CustomMapMessageCell -- (void)awakeFromNib { - [super awakeFromNib]; - // Initialization code -} - -- (void)setSelected:(BOOL)selected animated:(BOOL)animated { - [super setSelected:selected animated:animated]; - - // Configure the view for the selected state -} - - - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { - - [self createUI]; - - + [self createMapUI]; } return self; } -- (void)createUI { - - self.heardImage = [[UIImageView alloc] init]; - self.heardImage.backgroundColor = KWhiteColor; - self.heardImage.image = [UIImage imageNamed:@"heard_man"]; - [self.contentView addSubview:self.heardImage]; - self.heardImage.layer.cornerRadius = 20; - self.heardImage.layer.masksToBounds = true; - [self.heardImage mas_makeConstraints:^(MASConstraintMaker *make) { - - make.left.mas_equalTo(@15); - make.top.mas_equalTo(@8); - make.height.width.mas_equalTo(@30); - }]; - - self.nickNameLabel = [[UILabel alloc] init]; - self.nickNameLabel.text = @"全科医生"; - [self.contentView addSubview:self.nickNameLabel]; - self.nickNameLabel.font = SYSTEMFONT(12); - [self.nickNameLabel mas_makeConstraints:^(MASConstraintMaker *make) { - - make.left.mas_equalTo(self.heardImage.mas_right).offset(10); - make.top.mas_equalTo(@10); - make.width.mas_equalTo(@300.0); - make.height.mas_offset(13); - }]; - - //姓名 - self.backView = [[UIView alloc] init]; - self.backView.layer.cornerRadius = 10; - self.backView.backgroundColor = KWhiteColor; - [self.contentView addSubview:self.backView]; - - - [self.backView mas_makeConstraints:^(MASConstraintMaker *make) { - - make.left.mas_equalTo(self.heardImage.mas_right).offset(10); - make.width.mas_offset(245); - make.top.mas_equalTo(self.nickNameLabel.mas_bottom).offset(8); - make.height.mas_offset(170); - }]; - - +- (void)createMapUI { + + // 隐藏默认气泡图片,改用白色圆角 + self.bubbleView.image = nil; + self.myTextLabel = [[UILabel alloc] init]; self.myTextLabel.font = SYSTEMFONT(15); self.myTextLabel.textColor = CFontColor1; - self.myTextLabel.text = @"我的位置"; - [self.backView addSubview:self.myTextLabel]; - + self.myTextLabel.numberOfLines = 0; + [self.container addSubview:self.myTextLabel]; [self.myTextLabel mas_makeConstraints:^(MASConstraintMaker *make) { - make.left.mas_offset(10); - make.top.mas_offset(0); + make.top.mas_offset(10); make.right.mas_offset(-10); - make.height.mas_offset(25); - }]; - - + }]; + self.mapImage = [[UIImageView alloc] init]; self.mapImage.backgroundColor = KRedColor; - [self.backView addSubview:self.mapImage]; + [self.container addSubview:self.mapImage]; [self.mapImage mas_makeConstraints:^(MASConstraintMaker *make) { - make.left.right.bottom.mas_offset(0); make.top.mas_equalTo(self.myTextLabel.mas_bottom); }]; + + // 长按转发到微信 + // TUIChat 框架把长按手势注释掉了,这里重新加上。 + // 需要让 container 上已有的 tap 手势等 longPress 失败后再触发,否则 tap 会抢走触摸。 + UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongPress:)]; + longPress.minimumPressDuration = 0.5; + [self.container addGestureRecognizer:longPress]; + + for (UIGestureRecognizer *g in self.container.gestureRecognizers) { + if ([g isKindOfClass:[UITapGestureRecognizer class]]) { + [g requireGestureRecognizerToFail:longPress]; + } + } } +#pragma mark - TUIChat 实时聊天回调 + +- (void)fillWithData:(CustomMapMessageCellData *)data { + [super fillWithData:data]; + + self.myTextLabel.text = data.text.length > 0 ? data.text : @"我的位置"; + [self.mapImage sd_setImageWithURL:[NSURL URLWithString:data.MapPictureImg] placeholderImage:nil]; + + self.nameLabel.hidden = YES; +} + +#pragma mark - 历史记录回调 + - (void)setModel:(CustomTUIHistoryModel *)model { _model = model; - -// self.nickNameLabel.text = _model.fromAccountName; - - if (_model.isSelf == true) { //头像在右 - - [self.heardImage mas_remakeConstraints:^(MASConstraintMaker *make) { - + + self.myTextLabel.text = _model.address.length > 0 ? _model.address : @"我的位置"; + + NSString * MapPictureImg = [self mapLocationImage:_model.Longitude Withlation:_model.Latitude]; + [self.mapImage sd_setImageWithURL:[NSURL URLWithString:MapPictureImg] placeholderImage:nil]; + + // 历史记录:手动设置头像和名字 + self.avatarView.image = [UIImage imageNamed:@"heard_man"]; + self.nameLabel.text = @"全科医生"; + self.nameLabel.hidden = NO; + + if (_model.isSelf) { + self.avatarView.hidden = NO; + [self.avatarView mas_remakeConstraints:^(MASConstraintMaker *make) { make.height.width.mas_offset(40); make.top.mas_equalTo(@10); make.right.mas_equalTo(self.contentView.mas_right).offset(-10); - }]; - self.nickNameLabel.textAlignment = NSTextAlignmentRight; - [self.nickNameLabel mas_remakeConstraints:^(MASConstraintMaker *make) { - - make.right.mas_equalTo(self.heardImage.mas_left).offset(-10); + self.nameLabel.textAlignment = NSTextAlignmentRight; + [self.nameLabel mas_remakeConstraints:^(MASConstraintMaker *make) { + make.right.mas_equalTo(self.avatarView.mas_left).offset(-10); make.top.mas_equalTo(@10); make.width.mas_equalTo(@245.0); make.height.mas_offset(13); }]; - - [self.backView mas_remakeConstraints:^(MASConstraintMaker *make) { - make.top.mas_equalTo(self.nickNameLabel.mas_bottom).offset(8); + [self.container mas_remakeConstraints:^(MASConstraintMaker *make) { + make.top.mas_equalTo(self.nameLabel.mas_bottom).offset(8); make.width.mas_offset(245); - make.right.mas_equalTo(self.heardImage.mas_left).offset(-10); - make.height.mas_equalTo(@170); + make.right.mas_equalTo(self.avatarView.mas_left).offset(-10); + make.height.mas_offset(170); }]; - - - }else - { - [self.heardImage mas_remakeConstraints:^(MASConstraintMaker *make) { - + } else { + self.avatarView.hidden = NO; + [self.avatarView mas_remakeConstraints:^(MASConstraintMaker *make) { make.left.mas_equalTo(@15); make.top.mas_equalTo(@8); make.height.width.mas_equalTo(@40); }]; - self.nickNameLabel.textAlignment = NSTextAlignmentLeft; - - [self.nickNameLabel mas_remakeConstraints:^(MASConstraintMaker *make) { - - make.left.mas_equalTo(self.heardImage.mas_right).offset(10); + self.nameLabel.textAlignment = NSTextAlignmentLeft; + [self.nameLabel mas_remakeConstraints:^(MASConstraintMaker *make) { + make.left.mas_equalTo(self.avatarView.mas_right).offset(10); make.top.mas_equalTo(@10); make.width.mas_equalTo(@300.0); make.height.mas_offset(13); }]; - - - [self.backView mas_makeConstraints:^(MASConstraintMaker *make) { - - make.left.mas_equalTo(self.heardImage.mas_right).offset(10); + [self.container mas_remakeConstraints:^(MASConstraintMaker *make) { + make.left.mas_equalTo(self.avatarView.mas_right).offset(10); make.width.mas_offset(245); - make.top.mas_equalTo(self.nickNameLabel.mas_bottom).offset(8); + make.top.mas_equalTo(self.nameLabel.mas_bottom).offset(8); make.height.mas_offset(170); }]; - - } - - [self.contentView mas_updateConstraints:^(MASConstraintMaker *make) { - - make.top.left.right.mas_equalTo(@0); - make.bottom.mas_equalTo(self.backView.mas_bottom).offset(10); - }]; - - - NSString * MapPictureImg = [self mapLocationImage:_model.Longitude Withlation:_model.Latitude]; - - [self.mapImage sd_setImageWithURL:[NSURL URLWithString:MapPictureImg] placeholderImage:nil]; - - NSString * photo = [NSString stringWithFormat:@"%@%@",ResourceAddress,_model.avatar]; - - NSString * str1 = @"\\"; - - photo = [photo stringByReplacingOccurrencesOfString:str1 withString:@"/"]; - + // 加载头像 + NSString * photo = [NSString stringWithFormat:@"%@%@", ResourceAddress, _model.avatar]; + photo = [photo stringByReplacingOccurrencesOfString:@"\\" withString:@"/"]; NSString *encodedString3 = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)photo, (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL, kCFStringEncodingUTF8)); - - [self.heardImage sd_setImageWithURL:[NSURL URLWithString:encodedString3] placeholderImage:PLACEHOLD_IMG]; + [self.avatarView sd_setImageWithURL:[NSURL URLWithString:encodedString3] placeholderImage:PLACEHOLD_IMG]; } +#pragma mark - 地图静态图 + - (NSString *)mapLocationImage:(CGFloat)longLation Withlation:(CGFloat)lation { - // 0: self.longLation,self.lation - //https://a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png NSString * staciUrl = @"https://restapi.amap.com/v3/staticmap?key=e9926badd26aaa766d13439c88a83f2d"; NSString * mapKey = @"e9926badd26aaa766d13439c88a83f2d"; NSString * loctaionUrl = @"https://a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png"; - + NSString * needMdString = [NSString stringWithFormat:@"key=%@&location=%f,%f&markers=-1,%@,0:%f,%f&size=700*500&zoom=15124d58f7223ac9738312a8d187a21f2b",mapKey,longLation,lation,loctaionUrl,longLation,lation]; - - + NSString * mapUrl = [NSString stringWithFormat:@"%@&location=%f,%f&markers=-1,%@,0:%f,%f&size=700*500&zoom=15&sig=%@",staciUrl,longLation,lation,loctaionUrl,longLation,lation,[self md5:needMdString]]; - + return mapUrl; - - - } -//md加密 + - (NSString *)md5:(NSString *)str { const char *cStr = [str UTF8String]; unsigned char result[16]; - CC_MD5(cStr, strlen(cStr), result); // This is the md5 call + CC_MD5(cStr, strlen(cStr), result); return [NSString stringWithFormat: @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", result[0], result[1], result[2], result[3], @@ -233,8 +170,83 @@ ]; } +#pragma mark - 长按转发微信 +- (void)onLongPress:(UILongPressGestureRecognizer *)gesture { + if (gesture.state != UIGestureRecognizerStateBegan) return; + + if (![[XJWeChatManager sharedManager] isWeChatInstalled]) { + [EasyTextView showErrorText:@"未安装微信"]; + return; + } + + UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; + [alert addAction:[UIAlertAction actionWithTitle:@"转发到微信" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + [self shareToWeChat]; + }]]; + [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]]; + + UIViewController *topVC = [self topViewController]; + [topVC presentViewController:alert animated:YES completion:nil]; +} + +- (void)shareToWeChat { + NSString *address; + CGFloat lat = 0, lng = 0; + NSString *imageKey; + + // 实时聊天数据 + CustomMapMessageCellData *data = (CustomMapMessageCellData *)self.messageData; + if ([data isKindOfClass:[CustomMapMessageCellData class]]) { + address = data.text.length > 0 ? data.text : @"我的位置"; + lat = data.latitude; + lng = data.longitude; + imageKey = data.MapPictureImg; + } + // 历史记录数据 + else if (_model) { + address = _model.address.length > 0 ? _model.address : @"我的位置"; + lat = _model.Latitude; + lng = _model.Longitude; + imageKey = [self mapLocationImage:_model.Longitude Withlation:_model.Latitude]; + } else { + return; + } + + NSString *encodedAddress = [address stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; + NSString *mapUrl = [NSString stringWithFormat:@"https://uri.amap.com/marker?position=%f,%f&name=%@", + lng, lat, encodedAddress ?: @"位置"]; + + UIImage *thumb = [[SDImageCache sharedImageCache] imageFromCacheForKey:imageKey]; + + [[XJWeChatManager sharedManager] shareWebpageWithTitle:address + desc:[NSString stringWithFormat:@"经度:%.6f 纬度:%.6f", lng, lat] + webpageUrl:mapUrl + thumbImage:thumb + toScene:0 + completion:^(BOOL success, NSString * _Nullable errorMsg) { + if (!success && errorMsg.length > 0) { + [EasyTextView showErrorText:errorMsg]; + } + }]; +} + +- (UIViewController *)topViewController { + UIViewController *rootVC = [UIApplication sharedApplication].keyWindow.rootViewController; + while (rootVC.presentedViewController) { + rootVC = rootVC.presentedViewController; + } + if ([rootVC isKindOfClass:[UINavigationController class]]) { + return [(UINavigationController *)rootVC topViewController]; + } + if ([rootVC isKindOfClass:[UITabBarController class]]) { + UIViewController *selected = [(UITabBarController *)rootVC selectedViewController]; + if ([selected isKindOfClass:[UINavigationController class]]) { + return [(UINavigationController *)selected topViewController]; + } + return selected; + } + return rootVC; +} @end - - diff --git a/XinjiangProject/XinjiangProject/Project/CustomTUIChat/cellData/CustomMapMessageCellData.h b/XinjiangProject/XinjiangProject/Project/CustomTUIChat/cellData/CustomMapMessageCellData.h index f3f8cc8..c023aba 100644 --- a/XinjiangProject/XinjiangProject/Project/CustomTUIChat/cellData/CustomMapMessageCellData.h +++ b/XinjiangProject/XinjiangProject/Project/CustomTUIChat/cellData/CustomMapMessageCellData.h @@ -13,6 +13,7 @@ @property (nonatomic, copy) NSString *text; @property (nonatomic, copy) NSString * MapPictureImg; - +@property (nonatomic, assign) CGFloat latitude; +@property (nonatomic, assign) CGFloat longitude; @end diff --git a/XinjiangProject/XinjiangProject/Project/CustomTUIChat/cellData/CustomMapMessageCellData.m b/XinjiangProject/XinjiangProject/Project/CustomTUIChat/cellData/CustomMapMessageCellData.m index 37862c0..13d3167 100644 --- a/XinjiangProject/XinjiangProject/Project/CustomTUIChat/cellData/CustomMapMessageCellData.m +++ b/XinjiangProject/XinjiangProject/Project/CustomTUIChat/cellData/CustomMapMessageCellData.m @@ -16,6 +16,8 @@ cellData.innerMessage = message; cellData.msgID = message.msgID; cellData.text = message.locationElem.desc; + cellData.latitude = message.locationElem.latitude; + cellData.longitude = message.locationElem.longitude; cellData.MapPictureImg = [self mapLocationImage:message.locationElem.longitude Withlation:message.locationElem.latitude]; cellData.reuseId = @"MapMessageCustomCell"; cellData.showAvatar = true; diff --git a/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/PhysicalWebViewController.m b/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/PhysicalWebViewController.m index 8ed43e5..dc91d0b 100644 --- a/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/PhysicalWebViewController.m +++ b/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/PhysicalWebViewController.m @@ -15,17 +15,33 @@ - (void)viewDidLoad { [super viewDidLoad]; - // Do any additional setup after loading the view. + self.isHidenNaviBar = true; + [self.wkWebView mas_remakeConstraints:^(MASConstraintMaker *make) { + make.top.left.right.mas_offset(0); + make.bottom.mas_offset(-SafetyAreaHeight); + }]; } -/* -#pragma mark - Navigation +#pragma mark - WKNavigationDelegate -// In a storyboard-based application, you will often want to do a little preparation before navigation -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - // Get the new view controller using [segue destinationViewController]. - // Pass the selected object to the new view controller. +- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation { + [super webView:webView didStartProvisionalNavigation:navigation]; + DLog(@"[健康H5] 开始加载: %@", webView.URL.absoluteString); +} + +- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { + [super webView:webView didFinishNavigation:navigation]; + DLog(@"[健康H5] 加载完成: %@", webView.URL.absoluteString); +} + +- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error { + [super webView:webView didFailProvisionalNavigation:navigation withError:error]; + DLog(@"[健康H5] 加载失败: %@", error.localizedDescription); +} + +- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error { + [super webView:webView didFailNavigation:navigation withError:error]; + DLog(@"[健康H5] 导航失败: %@", error.localizedDescription); } -*/ @end diff --git a/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/XJBankWebDetailViewController.m b/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/XJBankWebDetailViewController.m index 634b074..bb7d3f0 100644 --- a/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/XJBankWebDetailViewController.m +++ b/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/XJBankWebDetailViewController.m @@ -25,13 +25,4 @@ // finishActivity 行为与基类一致(pop 返回),无需 override // 基类 XJBaseWebViewVC 已实现默认 pop 逻辑 -/* -#pragma mark - Navigation -// In a storyboard-based application, you will often want to do a little preparation before navigation -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - // Get the new view controller using [segue destinationViewController]. - // Pass the selected object to the new view controller. -} -*/ - @end diff --git a/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/XJHealthArchiveDetailViewController.h b/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/XJHealthArchiveDetailViewController.h new file mode 100644 index 0000000..24cb3af --- /dev/null +++ b/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/XJHealthArchiveDetailViewController.h @@ -0,0 +1,16 @@ +// +// XJHealthArchiveDetailViewController.h +// XinjiangProject +// +// 一人一案 - 健康档案查看页 +// + +#import "XJBaseViewController.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface XJHealthArchiveDetailViewController : XJBaseViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/XJHealthArchiveDetailViewController.m b/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/XJHealthArchiveDetailViewController.m new file mode 100644 index 0000000..e6e440f --- /dev/null +++ b/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/XJHealthArchiveDetailViewController.m @@ -0,0 +1,1124 @@ +// +// XJHealthArchiveDetailViewController.m +// XinjiangProject +// +// 一人一案 - 健康档案查看页(Figma 设计稿还原) +// +// ┌─────────────────────────────┐ +// │ 用户信息卡片(头像/姓名/身份)│ +// ├─────────────────────────────┤ +// │ 关爱管理信息 │ +// ├─────────────────────────────┤ +// │ 档案与标签 │ +// ├─────────────────────────────┤ +// │ 心血管健康 │ +// │ 代谢指标 │ +// │ 血脂四项 │ +// │ 肾脏与骨骼代谢 │ +// │ 特殊代谢指标 │ +// ├─────────────────────────────┤ +// │ 医疗意见与记录 │ +// └─────────────────────────────┘ +// + +#import "XJHealthArchiveDetailViewController.h" + +// 颜色(Figma 设计稿规范) +#define kGreenColor UIColorHex(0x2EB8B2) +#define kGreenLightColor UIColorHex(0xDFFFFC) +#define kDangerColor UIColorHex(0xF24439) +#define kWarningColor UIColorHex(0xfa8c16) +#define kNormalColor UIColorHex(0x2EB8B2) +#define kInfoLabelColor UIColorHex(0x808080) +#define kInfoValueColor UIColorHex(0x252535) +#define kMetricBgColor UIColorHex(0xF5F7FB) +#define kDividerColor UIColorHex(0xEAECF1) +#define kPageBgColor UIColorHex(0xF5F7FB) +#define kDarkTextColor UIColorHex(0x252535) +#define kGrayTextColor UIColorHex(0xB6B6B6) + +static const CGFloat kCardLR = 16; +#define kCardW (kScreenWidth - kCardLR * 2) +static const CGFloat kContentLR = 16; +#define kInnerW (kCardW - kContentLR * 2) + +@interface XJHealthArchiveDetailViewController () + +@property (nonatomic, strong) UIScrollView *scrollView; +@property (nonatomic, assign) CGFloat contentBottom; + +@end + +@implementation XJHealthArchiveDetailViewController + +#pragma mark - Life Cycle + +- (void)viewDidLoad { + [super viewDidLoad]; + self.navigationItem.title = @"一人一案"; + self.view.backgroundColor = kPageBgColor; + + if (@available(iOS 13.0, *)) { + UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init]; + [appearance configureWithOpaqueBackground]; + appearance.backgroundColor = KWhiteColor; + appearance.titleTextAttributes = @{NSForegroundColorAttributeName: UIColorHex(0x000000)}; + self.navigationController.navigationBar.standardAppearance = appearance; + self.navigationController.navigationBar.scrollEdgeAppearance = appearance; + } else { + self.navigationController.navigationBar.barTintColor = KWhiteColor; + } + self.navigationController.navigationBar.tintColor = UIColorHex(0x000000); + UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom]; + [backBtn setImage:[UIImage imageNamed:@"back"] forState:UIControlStateNormal]; + backBtn.frame = CGRectMake(0, 0, 30, 30); + [backBtn addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside]; + self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backBtn]; + + [self setupScrollView]; + [self buildHeader]; + self.contentBottom = [self buildCareInfo:self.contentBottom]; + self.contentBottom = [self buildArchive:self.contentBottom]; + self.contentBottom = [self buildCardiovascular:self.contentBottom]; + self.contentBottom = [self buildGlucose:self.contentBottom]; + self.contentBottom = [self buildLipid:self.contentBottom]; + self.contentBottom = [self buildKidney:self.contentBottom]; + self.contentBottom = [self buildSpecial:self.contentBottom]; + self.contentBottom = [self buildMedical:self.contentBottom]; + + [self updateContentSize]; +} + +- (void)viewDidLayoutSubviews { + [super viewDidLayoutSubviews]; + [self updateContentSize]; +} + +- (void)updateContentSize { + CGFloat bottomSafe = 0; +if (@available(iOS 11.0, *)) { + bottomSafe = self.view.safeAreaInsets.bottom; +} +self.scrollView.contentSize = CGSizeMake(kScreenWidth, self.contentBottom + 20 + bottomSafe); +} + +- (void)setupScrollView { + UIScrollView *sv = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight)]; + sv.backgroundColor = kPageBgColor; + sv.showsVerticalScrollIndicator = NO; + [self.view addSubview:sv]; + self.scrollView = sv; +} + +- (void)backAction { + [self.navigationController popViewControllerAnimated:YES]; +} + +#pragma mark - ————— 通用组件 ————— + +/// 白色圆角卡片 +- (UIView *)makeCard:(CGFloat)y { + UIView *card = [[UIView alloc] initWithFrame:CGRectMake(kCardLR, y, kCardW, 0)]; + card.backgroundColor = KWhiteColor; + card.layer.cornerRadius = 12; + card.layer.masksToBounds = YES; + [self.scrollView addSubview:card]; + return card; +} + +- (void)closeCard:(UIView *)card innerBottom:(CGFloat)ib { + card.frame = CGRectMake(card.frame.origin.x, card.frame.origin.y, card.frame.size.width, ib); +} + +- (void)placeView:(UIView *)v at:(CGFloat *)inner in:(UIView *)card { + v.frame = CGRectMake(v.frame.origin.x, *inner, v.frame.size.width, v.frame.size.height); + *inner += v.frame.size.height; + [card addSubview:v]; +} + +/// 卡片内标题行(绿色指示条 + 标题文字 + 可选右上角标签,高度48,底部分割线) +- (CGFloat)addCardTitle:(NSString *)title tagText:(NSString *)tagText inCard:(UIView *)card { + UIView *titleView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kCardW, 48)]; + + // 绿色指示条 + UIView *bar = [[UIView alloc] initWithFrame:CGRectMake(kContentLR, (48 - 10) / 2, 3, 10)]; + bar.backgroundColor = kGreenColor; + bar.layer.cornerRadius = 1.5; + [titleView addSubview:bar]; + + // 标题 + UILabel *tLbl = [[UILabel alloc] initWithFrame:CGRectMake(bar.frame.origin.x + 7, 0, kCardW - 120, 48)]; + tLbl.text = title; + tLbl.textColor = kDarkTextColor; + tLbl.font = [UIFont systemFontOfSize:16 weight:UIFontWeightHeavy]; + [titleView addSubview:tLbl]; + + // 右上角标签(可选) + if (tagText.length > 0) { + UILabel *tag = [[UILabel alloc] initWithFrame:CGRectMake(kCardW - kContentLR - 64, 12, 64, 24)]; + tag.text = tagText; + tag.textColor = kGreenColor; + tag.font = [UIFont systemFontOfSize:12 weight:UIFontWeightBold]; + tag.backgroundColor = [UIColor colorWithRed:0x2E/255.0 green:0xB8/255.0 blue:0xB2/255.0 alpha:0x1F/255.0]; + tag.layer.cornerRadius = 4; + tag.layer.masksToBounds = YES; + tag.textAlignment = NSTextAlignmentCenter; + [titleView addSubview:tag]; + } + + // 底部分割线 + UIView *div = [[UIView alloc] initWithFrame:CGRectMake(0, 47, kCardW, 1)]; + div.backgroundColor = kMetricBgColor; + [titleView addSubview:div]; + + [card addSubview:titleView]; + return 48; +} + +/// 半圆+虚线分隔线(左半圆直径10紧贴左边只露右半圆,右半圆紧贴右边只露左半圆,中间虚线连接) +- (UIView *)makeDashedSeparator:(CGFloat)width { + CGFloat h = 16; + UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, h)]; + container.clipsToBounds = NO; + + CGFloat circleD = 10; + CGFloat circleR = circleD / 2.0; + + // 左半圆 - 圆心在容器左边缘,左半被父视图裁掉只留右半圆,颜色跟身份证view底色一致 + UIView *leftCircle = [[UIView alloc] initWithFrame:CGRectMake(-circleR, (h - circleD) / 2.0, circleD, circleD)]; + leftCircle.backgroundColor = kMetricBgColor; + leftCircle.layer.cornerRadius = circleR; + leftCircle.layer.borderColor = kMetricBgColor.CGColor; + leftCircle.layer.borderWidth = 1; + [container addSubview:leftCircle]; + + // 右半圆 - 圆心在容器右边缘,右半被父视图裁掉只留左半圆 + UIView *rightCircle = [[UIView alloc] initWithFrame:CGRectMake(width - circleR, (h - circleD) / 2.0, circleD, circleD)]; + rightCircle.backgroundColor = kMetricBgColor; + rightCircle.layer.cornerRadius = circleR; + rightCircle.layer.borderColor = kMetricBgColor.CGColor; + rightCircle.layer.borderWidth = 1; + [container addSubview:rightCircle]; + + // 中间虚线 - 从左半圆右边缘到右半圆左边缘 + CGFloat dashX = circleR + 2; + CGFloat dashW = width - circleD - 4; + UIView *dashLine = [[UIView alloc] initWithFrame:CGRectMake(dashX, h / 2.0 - 0.5, dashW, 1)]; + [container addSubview:dashLine]; + + CAShapeLayer *dash = [CAShapeLayer layer]; + dash.frame = dashLine.bounds; + dash.strokeColor = kDividerColor.CGColor; + dash.lineWidth = 1; + dash.lineDashPattern = @[@4, @4]; + UIBezierPath *path = [UIBezierPath bezierPath]; + [path moveToPoint:CGPointMake(0, 0.5)]; + [path addLineToPoint:CGPointMake(dashW, 0.5)]; + dash.path = path.CGPath; + [dashLine.layer addSublayer:dash]; + + return container; +} + +/// 标签 +- (UILabel *)tagLabel:(NSString *)text color:(UIColor *)c bg:(UIColor *)bg { + CGFloat w = [text sizeWithAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:11 weight:UIFontWeightMedium]}].width + 16; + UILabel *t = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, w, 24)]; + t.text = text; + t.textColor = c; + t.font = [UIFont systemFontOfSize:11 weight:UIFontWeightMedium]; + t.backgroundColor = bg; + t.layer.cornerRadius = 6; + t.layer.masksToBounds = YES; + t.textAlignment = NSTextAlignmentCenter; + return t; +} + +/// 表格单元格 +- (UIView *)tableCell:(NSString *)text color:(UIColor *)c bg:(UIColor *)bg width:(CGFloat)w height:(CGFloat)h isFirst:(BOOL)isFirst isLast:(BOOL)isLast { + UIView *cell = [[UIView alloc] initWithFrame:CGRectMake(0, 0, w, h)]; + cell.backgroundColor = bg; + cell.layer.borderColor = UIColorHex(0xE6E6EA).CGColor; + cell.layer.borderWidth = 0.5; + + UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(2, 0, w - 4, h)]; + lbl.text = text; + lbl.textColor = c; + lbl.font = [UIFont systemFontOfSize:12 weight:UIFontWeightMedium]; + lbl.textAlignment = NSTextAlignmentCenter; + lbl.adjustsFontSizeToFitWidth = YES; + [cell addSubview:lbl]; + + return cell; +} + +/// 表头单元格 +- (UIView *)tableHeaderCell:(NSString *)text width:(CGFloat)w height:(CGFloat)h isFirst:(BOOL)isFirst isLast:(BOOL)isLast { + UIView *cell = [[UIView alloc] initWithFrame:CGRectMake(0, 0, w, h)]; + cell.backgroundColor = kMetricBgColor; + cell.layer.borderColor = UIColorHex(0xE6E6EA).CGColor; + cell.layer.borderWidth = 0.5; + + if (isFirst) { + cell.layer.cornerRadius = 4; + cell.layer.maskedCorners = kCALayerMinXMinYCorner; + } + if (isLast) { + cell.layer.cornerRadius = 4; + cell.layer.maskedCorners = kCALayerMaxXMinYCorner; + } + + UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(2, 0, w - 4, h)]; + lbl.text = text; + lbl.textColor = kDarkTextColor; + lbl.font = [UIFont systemFontOfSize:12 weight:UIFontWeightBold]; + lbl.textAlignment = NSTextAlignmentCenter; + [cell addSubview:lbl]; + + return cell; +} + +/// Figma 四列表格(表头+数据行,异常值用红色背景) +- (UIView *)makeFigmaTable:(NSArray *)rows + col1Header:(NSString *)h1 col2Header:(NSString *)h2 + col3Header:(NSString *)h3 col4Header:(NSString *)h4 { + CGFloat col1w = kInnerW * 0.24; + CGFloat col234w = (kInnerW - col1w) / 3.0; + CGFloat rh = 32; + CGFloat totalH = rh * (rows.count + 1); + + UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kInnerW, totalH)]; + + UIColor *abnormalBg = [UIColor colorWithRed:0xF2/255.0 green:0x44/255.0 blue:0x39/255.0 alpha:0x1A/255.0]; + UIColor *normalTextColor = UIColorHex(0x77849E); + + // 表头 + NSArray *headers = @[h1, h2, h3, h4]; + NSArray *widths = @[@(col1w), @(col234w), @(col234w), @(col234w)]; + CGFloat x = 0; + for (NSInteger i = 0; i < 4; i++) { + UIView *hc = [self tableHeaderCell:headers[i] width:[widths[i] floatValue] height:rh isFirst:(i==0) isLast:(i==3)]; + hc.frame = CGRectMake(x, 0, [widths[i] floatValue], rh); + [container addSubview:hc]; + x += [widths[i] floatValue] - 0.5; + } + + // 数据行 + for (NSInteger r = 0; r < rows.count; r++) { + NSDictionary *d = rows[r]; + CGFloat ry = rh + r * rh; + + // 项目名 —— 灰色字体 + UIView *nc = [self tableCell:d[@"name"] color:normalTextColor bg:KWhiteColor width:col1w height:rh isFirst:YES isLast:NO]; + nc.frame = CGRectMake(0, ry, col1w, rh); + [container addSubview:nc]; + + // 检测值 —— 异常时红色背景+红色字体 + UIColor *dcColor = d[@"detectColor"]; + BOOL dcAbnormal = [dcColor isEqual:kDangerColor] || [dcColor isEqual:kWarningColor]; + UIColor *dcBg = dcAbnormal ? abnormalBg : KWhiteColor; + UIColor *dcText = dcAbnormal ? kDangerColor : normalTextColor; + NSString *dvs = [NSString stringWithFormat:@"%@%@", d[@"detectVal"], d[@"unit"]]; + UIView *dc = [self tableCell:dvs color:dcText bg:dcBg width:col234w height:rh isFirst:NO isLast:NO]; + dc.frame = CGRectMake(col1w - 0.5, ry, col234w, rh); + [container addSubview:dc]; + + // 控制目标 + NSString *tvs = [NSString stringWithFormat:@"%@%@", d[@"targetVal"], d[@"unit"]]; + UIView *tc = [self tableCell:tvs color:kNormalColor bg:KWhiteColor width:col234w height:rh isFirst:NO isLast:NO]; + tc.frame = CGRectMake(col1w + col234w - 1, ry, col234w, rh); + [container addSubview:tc]; + + // 监测值 —— 异常时红色背景+红色字体 + UIColor *mcColor = d[@"monitorColor"] ?: normalTextColor; + BOOL mcAbnormal = [mcColor isEqual:kDangerColor] || [mcColor isEqual:kWarningColor]; + UIColor *mcBg = mcAbnormal ? abnormalBg : KWhiteColor; + UIColor *mcText = mcAbnormal ? kDangerColor : normalTextColor; + NSString *mvs = [NSString stringWithFormat:@"%@%@", d[@"monitorVal"], d[@"unit"]]; + UIView *mc = [self tableCell:mvs color:mcText bg:mcBg width:col234w height:rh isFirst:NO isLast:YES]; + mc.frame = CGRectMake(col1w + col234w * 2 - 1.5, ry, col234w, rh); + [container addSubview:mc]; + } + + return container; +} + +/// 意见文本块 +- (UIView *)textBlock:(NSString *)content leftColor:(UIColor *)lc author:(NSString *)author time:(NSString *)time { + CGFloat w = kInnerW; + CGFloat textW = w - 30; + CGFloat contentH = [content boundingRectWithSize:CGSizeMake(textW, CGFLOAT_MAX) + options:NSStringDrawingUsesLineFragmentOrigin + attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12]} + context:nil].size.height; + + CGFloat blockH = contentH + 48; + UIView *block = [[UIView alloc] initWithFrame:CGRectMake(0, 0, w, blockH)]; + block.backgroundColor = kMetricBgColor; + block.layer.cornerRadius = 10; + block.layer.masksToBounds = YES; + + UIView *bar = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 3, blockH)]; + bar.backgroundColor = lc; + [block addSubview:bar]; + + UILabel *cl = [[UILabel alloc] initWithFrame:CGRectMake(15, 12, textW, contentH)]; + cl.text = content; + cl.textColor = UIColorHex(0x555555); + cl.font = [UIFont systemFontOfSize:12]; + cl.numberOfLines = 0; + [block addSubview:cl]; + + UILabel *al = [[UILabel alloc] initWithFrame:CGRectMake(w - 260, contentH + 20, 120, 14)]; + al.text = [NSString stringWithFormat:@"填报人:%@", author]; + al.textColor = UIColorHex(0xbbbbbb); + al.font = [UIFont systemFontOfSize:11]; + al.textAlignment = NSTextAlignmentRight; + [block addSubview:al]; + + UILabel *tl = [[UILabel alloc] initWithFrame:CGRectMake(w - 130, contentH + 20, 120, 14)]; + tl.text = time; + tl.textColor = UIColorHex(0xbbbbbb); + tl.font = [UIFont systemFontOfSize:11]; + tl.textAlignment = NSTextAlignmentRight; + [block addSubview:tl]; + + return block; +} + + +#pragma mark - ————— 1. 用户信息卡片 ————— + +- (void)buildHeader { + UIView *card = [self makeCard:8]; + CGFloat cy = 0; + + // 绿色顶部条 + UIView *topBar = [[UIView alloc] initWithFrame:CGRectMake(0, cy, kCardW, 6)]; + topBar.backgroundColor = kGreenColor; + [card addSubview:topBar]; + cy += 6; + + // 姓名 —— 距左边20,距顶部22 + UILabel *nameLbl = [[UILabel alloc] initWithFrame:CGRectMake(20, 22, 160, 24)]; + nameLbl.text = @"艾比拜·牙库甫"; + nameLbl.textColor = kDarkTextColor; + nameLbl.font = [UIFont systemFontOfSize:18 weight:UIFontWeightHeavy]; + [card addSubview:nameLbl]; + + // 性别年龄 —— 相对姓名lab上下居中 + CGFloat gaH = 18; + CGFloat gaY = nameLbl.frame.origin.y + (nameLbl.frame.size.height - gaH) / 2.0; + UILabel *gaLbl = [[UILabel alloc] initWithFrame:CGRectMake(kCardW - kContentLR - 100, gaY, 100, gaH)]; + gaLbl.text = @"男性 · 52岁"; + gaLbl.textColor = kDarkTextColor; + gaLbl.font = [UIFont systemFontOfSize:14 weight:UIFontWeightHeavy]; + gaLbl.textAlignment = NSTextAlignmentRight; + [card addSubview:gaLbl]; + + // 档案已建立标签 —— 在姓名底部+8处 + UILabel *badge = [[UILabel alloc] initWithFrame:CGRectMake(kCardW - kContentLR - 84, nameLbl.frame.origin.y + nameLbl.frame.size.height + 8, 84, 24)]; + badge.text = @"档案已建立"; + badge.textColor = KWhiteColor; + badge.font = [UIFont systemFontOfSize:13 weight:UIFontWeightBold]; + badge.backgroundColor = kGreenColor; + badge.layer.cornerRadius = 4; + badge.layer.masksToBounds = YES; + badge.textAlignment = NSTextAlignmentCenter; + [card addSubview:badge]; + + // 公司 —— 距姓名lab底部+16 + CGFloat compY = nameLbl.frame.origin.y + nameLbl.frame.size.height + 16; + UILabel *compLbl = [[UILabel alloc] initWithFrame:CGRectMake(kContentLR, compY, kCardW - kContentLR * 2, 16)]; + compLbl.text = @"新疆油田分公司采油一厂"; + compLbl.textColor = kDarkTextColor; + compLbl.font = [UIFont systemFontOfSize:14 weight:UIFontWeightBold]; + [card addSubview:compLbl]; + + // 半圆+虚线分隔线 —— 公司lab底部+12,满宽 + cy = compY + 16 + 12; + UIView *dashSep = [self makeDashedSeparator:kCardW]; + dashSep.frame = CGRectMake(0, cy, kCardW, 16); + [card addSubview:dashSep]; + + // 灰色信息区 —— 虚线底部+12 + cy += 16 + 12; + CGFloat infoW = kCardW - kContentLR * 2; + UIView *infoBg = [[UIView alloc] initWithFrame:CGRectMake(kContentLR, cy, infoW, 78)]; + infoBg.backgroundColor = kMetricBgColor; + infoBg.layer.cornerRadius = 12; + [card addSubview:infoBg]; + + // 身份证号 + UILabel *idTitle = [[UILabel alloc] initWithFrame:CGRectMake(12, 14, 70, 20)]; + idTitle.text = @"身份证号"; + idTitle.textColor = kInfoLabelColor; + idTitle.font = [UIFont systemFontOfSize:13 weight:UIFontWeightMedium]; + [infoBg addSubview:idTitle]; + + UILabel *idVal = [[UILabel alloc] initWithFrame:CGRectMake(infoW - 12 - 200, 14, 200, 20)]; + idVal.text = @"650202197403151234"; + idVal.textColor = kInfoValueColor; + idVal.font = [UIFont systemFontOfSize:13 weight:UIFontWeightMedium]; + idVal.textAlignment = NSTextAlignmentRight; + [infoBg addSubview:idVal]; + + // 手机号 + UILabel *phTitle = [[UILabel alloc] initWithFrame:CGRectMake(12, 44, 70, 20)]; + phTitle.text = @"手机号"; + phTitle.textColor = kInfoLabelColor; + phTitle.font = [UIFont systemFontOfSize:13 weight:UIFontWeightMedium]; + [infoBg addSubview:phTitle]; + + UILabel *phVal = [[UILabel alloc] initWithFrame:CGRectMake(infoW - 12 - 200, 44, 200, 20)]; + phVal.text = @"157****5522"; + phVal.textColor = kInfoValueColor; + phVal.font = [UIFont systemFontOfSize:13 weight:UIFontWeightMedium]; + phVal.textAlignment = NSTextAlignmentRight; + [infoBg addSubview:phVal]; + + cy += 78 + 16; + [self closeCard:card innerBottom:cy]; + self.contentBottom = card.frame.origin.y + card.frame.size.height + 12; +} + +#pragma mark - ————— 2. 关爱管理信息 ————— + +- (CGFloat)buildCareInfo:(CGFloat)y { + UIView *card = [self makeCard:y]; + CGFloat inner = 0; + + // 标题行(含"重点关爱"右上角标签) + [self addCardTitle:@"关爱管理信息" tagText:@"重点关爱" inCard:card]; + inner = 48; + + // 关爱联络员 + UIView *contactRow = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kCardW, 48)]; + UILabel *cl = [[UILabel alloc] initWithFrame:CGRectMake(kContentLR, 0, 80, 48)]; + cl.text = @"关爱联络员"; + cl.textColor = kInfoLabelColor; + cl.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium]; + [contactRow addSubview:cl]; + + UILabel *cv = [[UILabel alloc] initWithFrame:CGRectMake(kCardW - kContentLR - 200, 0, 200, 48)]; + cv.text = @"陈沐辰"; + cv.textColor = kDarkTextColor; + cv.font = [UIFont systemFontOfSize:13 weight:UIFontWeightMedium]; + cv.textAlignment = NSTextAlignmentRight; + [contactRow addSubview:cv]; + [self placeView:contactRow at:&inner in:card]; + + // 分割线 + UIView *div = [[UIView alloc] initWithFrame:CGRectMake(kContentLR, 0, kInnerW, 1)]; + div.backgroundColor = kDividerColor; + [self placeView:div at:&inner in:card]; + + // 负面清单 + UIView *negRow = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kCardW, 48)]; + UILabel *nl = [[UILabel alloc] initWithFrame:CGRectMake(kContentLR, 0, 80, 48)]; + nl.text = @"负面清单"; + nl.textColor = kInfoLabelColor; + nl.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium]; + [negRow addSubview:nl]; + + UILabel *nv = [[UILabel alloc] initWithFrame:CGRectMake(kCardW - kContentLR - 200, 0, 200, 48)]; + nv.text = @"胃炎、高尿酸、负重关节炎"; + nv.textColor = UIColorHex(0x77849E); + nv.font = [UIFont systemFontOfSize:13 weight:UIFontWeightMedium]; + nv.textAlignment = NSTextAlignmentRight; + [negRow addSubview:nv]; + [self placeView:negRow at:&inner in:card]; + + [self closeCard:card innerBottom:inner]; + return card.frame.origin.y + card.frame.size.height + 12; +} + +#pragma mark - ————— 3. 档案与标签 ————— + +- (CGFloat)buildArchive:(CGFloat)y { + UIView *card = [self makeCard:y]; + CGFloat inner = 0; + + [self addCardTitle:@"档案与标签" tagText:@"" inCard:card]; + inner = 48; + + // 普通虚线(非半圆)—— 标题底部 + UIView *dashLine = [[UIView alloc] initWithFrame:CGRectMake(kContentLR, inner, kInnerW, 1)]; + CAShapeLayer *dash = [CAShapeLayer layer]; + dash.frame = dashLine.bounds; + dash.strokeColor = kDividerColor.CGColor; + dash.lineWidth = 1; + dash.lineDashPattern = @[@4, @4]; + UIBezierPath *path = [UIBezierPath bezierPath]; + [path moveToPoint:CGPointMake(0, 0.5)]; + [path addLineToPoint:CGPointMake(kInnerW, 0.5)]; + dash.path = path.CGPath; + [dashLine.layer addSublayer:dash]; + [card addSubview:dashLine]; + + // 体重展示区 —— 虚线底部+20 + inner += 1 + 20; + CGFloat weightAreaH = 48; + UIView *weightRow = [[UIView alloc] initWithFrame:CGRectMake(0, inner, kCardW, weightAreaH)]; + + // 左侧:体重数值 + 单位(自适应宽度) + UILabel *wgt = [[UILabel alloc] init]; + wgt.text = @"68.20"; + wgt.textColor = kDarkTextColor; + wgt.font = [UIFont systemFontOfSize:40 weight:UIFontWeightHeavy]; + [wgt sizeToFit]; + wgt.frame = CGRectMake(kContentLR, 0, wgt.frame.size.width, 40); + [weightRow addSubview:wgt]; + + UILabel *wUnit = [[UILabel alloc] init]; + wUnit.text = @"kg"; + wUnit.textColor = kInfoLabelColor; + wUnit.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium]; + [wUnit sizeToFit]; + wUnit.frame = CGRectMake(CGRectGetMaxX(wgt.frame) + 4, 26, wUnit.frame.size.width, wUnit.frame.size.height); + [weightRow addSubview:wUnit]; + + // 右侧:体重变化区 —— 整体在卡片右半边居中 + UILabel *wChange = [[UILabel alloc] init]; + wChange.text = @"2.6"; + wChange.textColor = kGreenColor; + wChange.font = [UIFont systemFontOfSize:24 weight:UIFontWeightHeavy]; + [wChange sizeToFit]; + + UILabel *wChangeUnit = [[UILabel alloc] init]; + wChangeUnit.text = @"kg"; + wChangeUnit.textColor = kGreenColor; + wChangeUnit.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium]; + [wChangeUnit sizeToFit]; + + // 上半行总宽:2.6 + kg + CGFloat topRowW = wChange.frame.size.width + wChangeUnit.frame.size.width; + + UILabel *wTip = [[UILabel alloc] init]; + wTip.text = @"体重较上次减轻"; + wTip.textColor = kGrayTextColor; + wTip.font = [UIFont systemFontOfSize:12 weight:UIFontWeightMedium]; + [wTip sizeToFit]; + + // 取上下两行较宽者 + CGFloat blockW = MAX(topRowW, wTip.frame.size.width); + CGFloat rightHalfCenterX = kCardW / 2.0 + (kCardW - kContentLR - kCardW / 2.0) / 2.0; + CGFloat blockX = rightHalfCenterX - blockW / 2.0; + + // 2.6 + wChange.frame = CGRectMake(blockX, 2, wChange.frame.size.width, 24); + [weightRow addSubview:wChange]; + + // kg(紧跟2.6) + wChangeUnit.frame = CGRectMake(CGRectGetMaxX(wChange.frame), 10, wChangeUnit.frame.size.width, wChangeUnit.frame.size.height); + [weightRow addSubview:wChangeUnit]; + + // 箭头在 kg 上方居中 + UILabel *arrowLbl = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 14, 12)]; + arrowLbl.text = @"↑"; + arrowLbl.textColor = kGreenColor; + arrowLbl.font = [UIFont systemFontOfSize:12 weight:UIFontWeightBold]; + arrowLbl.textAlignment = NSTextAlignmentCenter; + CGFloat kgCenterX = wChangeUnit.frame.origin.x + wChangeUnit.frame.size.width / 2.0; + arrowLbl.frame = CGRectMake(kgCenterX - 7, 0, 14, 12); + [weightRow addSubview:arrowLbl]; + + // 体重较上次减轻(居中于block) + wTip.frame = CGRectMake(blockX, 28, blockW, wTip.frame.size.height); + wTip.textAlignment = NSTextAlignmentCenter; + [weightRow addSubview:wTip]; + + [self placeView:weightRow at:&inner in:card]; + + // 日期 —— 体重区域底部+8 + inner += 8; + UILabel *wDate = [[UILabel alloc] initWithFrame:CGRectMake(kContentLR, inner, 120, 14)]; + wDate.text = @"06-25 09:30"; + wDate.textColor = kGrayTextColor; + wDate.font = [UIFont systemFontOfSize:12 weight:UIFontWeightMedium]; + [card addSubview:wDate]; + inner += 14; + + // 实线分割线 —— 日期底部+12 + inner += 12; + UIView *div = [[UIView alloc] initWithFrame:CGRectMake(kContentLR, inner, kInnerW, 1)]; + div.backgroundColor = kDividerColor; + [card addSubview:div]; + inner += 1; + + // 三列卡片 —— 分割线底部+12,高度60 + inner += 12; + CGFloat colW = (kInnerW - 8) / 3.0; + CGFloat colH = 60; + + // 判断 BMI 是否标准(18.5~24 为标准) + CGFloat bmiVal = 22.4; + BOOL bmiIsStandard = (bmiVal >= 18.5 && bmiVal < 24.0); + + // 卡片1:身高(值+单位居中) + UIView *col1 = [[UIView alloc] initWithFrame:CGRectMake(kContentLR, inner, colW, colH)]; + col1.backgroundColor = kMetricBgColor; + col1.layer.cornerRadius = 8; + [card addSubview:col1]; + + UILabel *t1 = [[UILabel alloc] initWithFrame:CGRectMake(0, 8, colW, 14)]; + t1.text = @"身高"; + t1.textColor = kInfoLabelColor; + t1.font = [UIFont systemFontOfSize:13 weight:UIFontWeightMedium]; + t1.textAlignment = NSTextAlignmentCenter; + [col1 addSubview:t1]; + + // "172" + "cm" 整体居中 + UILabel *v1 = [[UILabel alloc] init]; + v1.text = @"172"; + v1.textColor = kDarkTextColor; + v1.font = [UIFont systemFontOfSize:22 weight:UIFontWeightHeavy]; + [v1 sizeToFit]; + UILabel *v1Unit = [[UILabel alloc] init]; + v1Unit.text = @"cm"; + v1Unit.textColor = kInfoLabelColor; + v1Unit.font = [UIFont systemFontOfSize:13 weight:UIFontWeightHeavy]; + [v1Unit sizeToFit]; + CGFloat v1TotalW = v1.frame.size.width + v1Unit.frame.size.width; + v1.frame = CGRectMake((colW - v1TotalW) / 2.0, 28, v1.frame.size.width, 22); + v1Unit.frame = CGRectMake(v1.frame.origin.x + v1.frame.size.width, 36, v1Unit.frame.size.width, 14); + [col1 addSubview:v1]; + [col1 addSubview:v1Unit]; + + // 卡片2:BMI + 标题后跟状态标签 + UIView *col2 = [[UIView alloc] initWithFrame:CGRectMake(kContentLR + colW + 4, inner, colW, colH)]; + col2.backgroundColor = kMetricBgColor; + col2.layer.cornerRadius = 8; + [card addSubview:col2]; + + // BMI 标题 + 状态标签(整体居中) + CGFloat bmiTitleW = 30; + CGFloat bmiBadgeW = 24; + + UILabel *t2 = [[UILabel alloc] initWithFrame:CGRectMake((colW - bmiTitleW - bmiBadgeW - 4) / 2.0, 8, bmiTitleW, 14)]; + t2.text = @"BMI"; + t2.textColor = kInfoLabelColor; + t2.font = [UIFont systemFontOfSize:13 weight:UIFontWeightMedium]; + [col2 addSubview:t2]; + + // BMI 状态标签紧跟在标题右边 + UILabel *bmiStatus = [[UILabel alloc] initWithFrame:CGRectMake(t2.frame.origin.x + bmiTitleW + 4, 8, bmiBadgeW, 15)]; + bmiStatus.text = bmiIsStandard ? @"标准" : @"异常"; + bmiStatus.textColor = KWhiteColor; + bmiStatus.font = [UIFont systemFontOfSize:9 weight:UIFontWeightMedium]; + bmiStatus.backgroundColor = bmiIsStandard ? kGreenColor : kDangerColor; + bmiStatus.layer.cornerRadius = 3; + bmiStatus.layer.masksToBounds = YES; + bmiStatus.textAlignment = NSTextAlignmentCenter; + [col2 addSubview:bmiStatus]; + + // BMI 数值居中 + UILabel *v2 = [[UILabel alloc] initWithFrame:CGRectMake(0, 26, colW, 26)]; + v2.text = @"22.4"; + v2.textColor = kDarkTextColor; + v2.font = [UIFont systemFontOfSize:22 weight:UIFontWeightHeavy]; + v2.textAlignment = NSTextAlignmentCenter; + [col2 addSubview:v2]; + + // 卡片3:标准体重 + UIView *col3 = [[UIView alloc] initWithFrame:CGRectMake(kContentLR + (colW + 4) * 2, inner, colW, colH)]; + col3.backgroundColor = kMetricBgColor; + col3.layer.cornerRadius = 8; + [card addSubview:col3]; + + UILabel *t3 = [[UILabel alloc] initWithFrame:CGRectMake(0, 8, colW, 14)]; + t3.text = @"标准体重"; + t3.textColor = kInfoLabelColor; + t3.font = [UIFont systemFontOfSize:13 weight:UIFontWeightMedium]; + t3.textAlignment = NSTextAlignmentCenter; + [col3 addSubview:t3]; + + // "52.6" + "kg" 整体居中 + UILabel *v3 = [[UILabel alloc] init]; + v3.text = @"52.6"; + v3.textColor = kDarkTextColor; + v3.font = [UIFont systemFontOfSize:22 weight:UIFontWeightHeavy]; + [v3 sizeToFit]; + UILabel *v3Unit = [[UILabel alloc] init]; + v3Unit.text = @"kg"; + v3Unit.textColor = kInfoLabelColor; + v3Unit.font = [UIFont systemFontOfSize:13 weight:UIFontWeightHeavy]; + [v3Unit sizeToFit]; + CGFloat v3TotalW = v3.frame.size.width + v3Unit.frame.size.width; + v3.frame = CGRectMake((colW - v3TotalW) / 2.0, 28, v3.frame.size.width, 22); + v3Unit.frame = CGRectMake(v3.frame.origin.x + v3.frame.size.width, 36, v3Unit.frame.size.width, 14); + [col3 addSubview:v3]; + [col3 addSubview:v3Unit]; + + inner += colH + 12; + + [self closeCard:card innerBottom:inner]; + return card.frame.origin.y + card.frame.size.height + 12; +} + +/// 健康指标图标+文字行(文字14 Bold紧跟右侧) +- (CGFloat)addHealthIconRow:(NSString *)imageName label:(NSString *)labelText iconSize:(CGSize)iconSize atY:(CGFloat)y inCard:(UIView *)card { + UIImageView *icon = [[UIImageView alloc] initWithFrame:CGRectMake(kContentLR, y, iconSize.width, iconSize.height)]; + icon.image = [UIImage imageNamed:imageName]; + icon.contentMode = UIViewContentModeScaleAspectFit; + [card addSubview:icon]; + + UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(kContentLR + iconSize.width + 6, y + (iconSize.height - 20) / 2.0, 100, 20)]; + lbl.text = labelText; + lbl.textColor = kDarkTextColor; + lbl.font = [UIFont systemFontOfSize:14 weight:UIFontWeightBold]; + [card addSubview:lbl]; + + return y + iconSize.height; +} + +#pragma mark - ————— 4. 心血管健康 ————— + +- (CGFloat)buildCardiovascular:(CGFloat)y { + UIView *card = [self makeCard:y]; + CGFloat inner = 0; + + [self addCardTitle:@"心血管健康" tagText:@"" inCard:card]; + inner = 48; + + // 图标+文字行 —— 分割线底部+10 + inner += 10; + CGFloat iconBottom = [self addHealthIconRow:@"health_icon_bp" label:@"血压" iconSize:CGSizeMake(23, 26) atY:inner inCard:card]; + + // 右侧血压状态lab —— 距右边-16,距分割线底部16 + NSString *statusText = @"高血压"; + UIFont *statusFont = [UIFont systemFontOfSize:12 weight:UIFontWeightMedium]; + CGFloat statusW = [statusText sizeWithAttributes:@{NSFontAttributeName: statusFont}].width + 8; + CGFloat statusY = 48 + 16; + CGFloat statusH = 20; + UILabel *statusLbl = [[UILabel alloc] initWithFrame:CGRectMake(kCardW - kContentLR - statusW, statusY, statusW, statusH)]; + statusLbl.text = statusText; + statusLbl.textColor = kDarkTextColor; + statusLbl.font = statusFont; + [card addSubview:statusLbl]; + + // 状态图标 —— 状态lab左边-4,上下居中,8x8 + CGFloat dotW = 8; + UIImageView *statusDot = [[UIImageView alloc] initWithFrame:CGRectMake(statusLbl.frame.origin.x - dotW - 4, statusY + (statusH - dotW) / 2.0, dotW, dotW)]; + statusDot.image = [UIImage imageNamed:@"health_status_dot"]; + statusDot.contentMode = UIViewContentModeScaleAspectFit; + [card addSubview:statusDot]; + + // 表格 —— 图标底部+8 + inner = iconBottom + 8; + NSArray *rows = @[ + @{@"name": @"收缩压", @"detectVal": @"76", @"detectColor": kWarningColor, + @"targetVal": @"<70", @"monitorVal": @"78", @"monitorColor": kDangerColor, @"unit": @"mmHg"}, + @{@"name": @"舒张压", @"detectVal": @"145", @"detectColor": kWarningColor, + @"targetVal": @"<145", @"monitorVal": @"148", @"monitorColor": UIColorHex(0x77849E), @"unit": @"mmHg"}, + ]; + UIView *table = [self makeFigmaTable:rows col1Header:@"项目" col2Header:@"平均值" col3Header:@"控制目标" col4Header:@"监测值"]; + table.frame = CGRectMake(kContentLR, inner, kInnerW, table.frame.size.height); + [card addSubview:table]; + inner += table.frame.size.height + 16; + + [self closeCard:card innerBottom:inner]; + return card.frame.origin.y + card.frame.size.height + 12; +} + +#pragma mark - ————— 5. 代谢指标 ————— + +- (CGFloat)buildGlucose:(CGFloat)y { + UIView *card = [self makeCard:y]; + CGFloat inner = 0; + + [self addCardTitle:@"代谢指标" tagText:@"" inCard:card]; + inner = 48; + + // 图标+文字行 —— 分割线底部+10 + inner += 10; + CGFloat iconBottom = [self addHealthIconRow:@"health_icon_glu" label:@"血糖" iconSize:CGSizeMake(23, 26) atY:inner inCard:card]; + + // 右侧状态lab + NSString *statusText = @"糖尿病"; + UIFont *statusFont = [UIFont systemFontOfSize:12 weight:UIFontWeightMedium]; + CGFloat statusW = [statusText sizeWithAttributes:@{NSFontAttributeName: statusFont}].width + 8; + CGFloat statusY = 48 + 16; + CGFloat statusH = 20; + UILabel *statusLbl = [[UILabel alloc] initWithFrame:CGRectMake(kCardW - kContentLR - statusW, statusY, statusW, statusH)]; + statusLbl.text = statusText; + statusLbl.textColor = kDarkTextColor; + statusLbl.font = statusFont; + [card addSubview:statusLbl]; + + // 状态图标 —— 8x8 + CGFloat dotW = 8; + UIImageView *statusDot = [[UIImageView alloc] initWithFrame:CGRectMake(statusLbl.frame.origin.x - dotW - 4, statusY + (statusH - dotW) / 2.0, dotW, dotW)]; + statusDot.image = [UIImage imageNamed:@"health_status_dot"]; + statusDot.contentMode = UIViewContentModeScaleAspectFit; + [card addSubview:statusDot]; + + // 表格 —— 图标底部+8 + inner = iconBottom + 8; + NSArray *rows = @[ + @{@"name": @"空腹血糖", @"detectVal": @"76", @"detectColor": UIColorHex(0x77849E), + @"targetVal": @"<70", @"monitorVal": @"78", @"monitorColor": UIColorHex(0x77849E), @"unit": @"mmol/L"}, + ]; + UIView *table = [self makeFigmaTable:rows col1Header:@"项目" col2Header:@"GLU检测值" col3Header:@"控制目标" col4Header:@"监测值"]; + table.frame = CGRectMake(kContentLR, inner, kInnerW, table.frame.size.height); + [card addSubview:table]; + inner += table.frame.size.height + 16; + + [self closeCard:card innerBottom:inner]; + return card.frame.origin.y + card.frame.size.height + 12; +} + +#pragma mark - ————— 6. 血脂四项 ————— + +- (CGFloat)buildLipid:(CGFloat)y { + UIView *card = [self makeCard:y]; + CGFloat inner = 0; + + [self addCardTitle:@"血脂四项" tagText:@"" inCard:card]; + inner = 48; + + // 图标+文字行 —— 分割线底部+10 + inner += 10; + CGFloat iconBottom = [self addHealthIconRow:@"health_icon_lipid" label:@"血脂" iconSize:CGSizeMake(23, 26) atY:inner inCard:card]; + + // 表格 —— 图标底部+8 + inner = iconBottom + 8; + NSArray *rows = @[ + @{@"name": @"低密度脂蛋白\n胆固醇", @"detectVal": @"3.8", @"detectColor": UIColorHex(0x77849E), + @"targetVal": @"<3.8", @"monitorVal": @"3.8", @"monitorColor": UIColorHex(0x77849E), @"unit": @"mmol/L"}, + @{@"name": @"高密度脂蛋白\n胆固醇", @"detectVal": @"3.8", @"detectColor": UIColorHex(0x77849E), + @"targetVal": @"<3.8", @"monitorVal": @"3.8", @"monitorColor": UIColorHex(0x77849E), @"unit": @"mmol/L"}, + @{@"name": @"甘油三酯", @"detectVal": @"3.8", @"detectColor": UIColorHex(0x77849E), + @"targetVal": @"<3.8", @"monitorVal": @"3.8", @"monitorColor": UIColorHex(0x77849E), @"unit": @"mmol/L"}, + @{@"name": @"总胆固醇", @"detectVal": @"3.8", @"detectColor": UIColorHex(0x77849E), + @"targetVal": @"<3.8", @"monitorVal": @"3.8", @"monitorColor": UIColorHex(0x77849E), @"unit": @"mmol/L"}, + ]; + UIView *table = [self makeFigmaTable:rows col1Header:@"项目" col2Header:@"检测值" col3Header:@"控制目标" col4Header:@"监测值"]; + table.frame = CGRectMake(kContentLR, inner, kInnerW, table.frame.size.height); + [card addSubview:table]; + inner += table.frame.size.height + 16; + + [self closeCard:card innerBottom:inner]; + return card.frame.origin.y + card.frame.size.height + 12; +} + +#pragma mark - ————— 7. 肾脏与骨骼代谢 ————— + +- (CGFloat)buildKidney:(CGFloat)y { + UIView *card = [self makeCard:y]; + CGFloat inner = 0; + + [self addCardTitle:@"肾脏与骨骼代谢" tagText:@"" inCard:card]; + inner = 48; + + // 图标+文字行 —— 分割线底部+10 + inner += 10; + CGFloat iconBottom = [self addHealthIconRow:@"health_icon_ua" label:@"尿酸" iconSize:CGSizeMake(30, 27) atY:inner inCard:card]; + + // 表格 —— 图标底部+8 + inner = iconBottom + 8; + NSArray *rows = @[ + @{@"name": @"UA", @"detectVal": @"486", @"detectColor": UIColorHex(0x77849E), + @"targetVal": @"<420", @"monitorVal": @"468", @"monitorColor": UIColorHex(0x77849E), @"unit": @"μmol/L"}, + ]; + UIView *table = [self makeFigmaTable:rows col1Header:@"项目" col2Header:@"GLU检测值" col3Header:@"控制目标" col4Header:@"监测值"]; + table.frame = CGRectMake(kContentLR, inner, kInnerW, table.frame.size.height); + [card addSubview:table]; + inner += table.frame.size.height + 16; + + [self closeCard:card innerBottom:inner]; + return card.frame.origin.y + card.frame.size.height + 12; +} + +#pragma mark - ————— 8. 特殊代谢指标 ————— + +- (CGFloat)buildSpecial:(CGFloat)y { + UIView *card = [self makeCard:y]; + CGFloat inner = 0; + + [self addCardTitle:@"特殊代谢指标" tagText:@"" inCard:card]; + inner = 48; + + // 直接放表格 —— 分割线底部+10 + inner += 10; + NSArray *rows = @[ + @{@"name": @"同型半胱氨酸", @"detectVal": @"3.8", @"detectColor": UIColorHex(0x77849E), + @"targetVal": @"<3.8", @"monitorVal": @"3.8", @"monitorColor": UIColorHex(0x77849E), @"unit": @"mmol/L"}, + ]; + UIView *table = [self makeFigmaTable:rows col1Header:@"项目" col2Header:@"检测值" col3Header:@"控制目标" col4Header:@"监测值"]; + table.frame = CGRectMake(kContentLR, inner, kInnerW, table.frame.size.height); + [card addSubview:table]; + inner += table.frame.size.height + 16; + + [self closeCard:card innerBottom:inner]; + return card.frame.origin.y + card.frame.size.height + 12; +} + +#pragma mark - ————— 9. 医疗意见与记录 ————— + +/// 创建意见内容view(白色圆角卡片,label 四周留边距) +- (UIView *)makeOpinionContent:(NSString *)content { + CGFloat pad = 16; + CGFloat textW = kCardW - pad * 2; + CGFloat contentH = [content boundingRectWithSize:CGSizeMake(textW, CGFLOAT_MAX) + options:NSStringDrawingUsesLineFragmentOrigin + attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12]} + context:nil].size.height; + UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kCardW, contentH + pad * 2)]; + container.backgroundColor = KWhiteColor; + container.layer.cornerRadius = 12; + UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(pad, pad, textW, contentH)]; + lbl.text = content; + lbl.textColor = UIColorHex(0x555555); + lbl.font = [UIFont systemFontOfSize:12]; + lbl.numberOfLines = 0; + [container addSubview:lbl]; + return container; +} + +#pragma mark - ————— 9. 医疗意见与记录 ————— + +- (CGFloat)buildMedical:(CGFloat)y { + UIView *card = [self makeCard:y]; + + [self addCardTitle:@"医疗意见与记录" tagText:@"" inCard:card]; + CGFloat inner = 48 + 16; + + // ==================== 小屋意见 Header ==================== + CGFloat wh = kCardW; + CGFloat connW = 16, connH = 42, connX = wh - kContentLR - connW; + + // 居中标题 "小屋意见" + UILabel *t1 = [[UILabel alloc] init]; + t1.text = @"小屋意见"; + t1.textColor = kGreenColor; + t1.font = [UIFont systemFontOfSize:15 weight:UIFontWeightBold]; + [t1 sizeToFit]; + t1.frame = CGRectMake((wh - t1.frame.size.width) / 2.0, inner, t1.frame.size.width, 20); + [card addSubview:t1]; + + // 左右装饰线(图片) + CGFloat lineY = inner + 9; + CGFloat lSpace = t1.frame.origin.x - 27 - kContentLR; + CGFloat rSpace = (wh - kContentLR) - (t1.frame.origin.x + t1.frame.size.width + 27); + CGFloat lineW = MIN(lSpace, rSpace); + if (lineW > 0) { + UIImageView *ll = [[UIImageView alloc] initWithFrame:CGRectMake(kContentLR, lineY, lineW, 1)]; + ll.image = [UIImage imageNamed:@"opinion_line_left"]; + [card addSubview:ll]; + } + if (lineW > 0) { + CGFloat rx = t1.frame.origin.x + t1.frame.size.width + 27; + UIImageView *rl = [[UIImageView alloc] initWithFrame:CGRectMake(rx, lineY, lineW, 1)]; + rl.image = [UIImage imageNamed:@"opinion_line_right"]; + [card addSubview:rl]; + } + + // 填报人 + 时间 + inner = t1.frame.origin.y + t1.frame.size.height + 12; + UILabel *a1 = [[UILabel alloc] initWithFrame:CGRectMake(kContentLR, inner, wh - 120, 16)]; + a1.text = @"填报人:李秀芳(健康管理师)"; + a1.textColor = kGrayTextColor; + a1.font = [UIFont systemFontOfSize:11 weight:UIFontWeightMedium]; + [card addSubview:a1]; + + UILabel *tm1 = [[UILabel alloc] initWithFrame:CGRectMake(wh - kContentLR - 160, inner, 160, 16)]; + tm1.text = @"2026-05-15 14:30"; + tm1.textColor = kGrayTextColor; + tm1.font = [UIFont systemFontOfSize:11 weight:UIFontWeightMedium]; + tm1.textAlignment = NSTextAlignmentRight; + [card addSubview:tm1]; + + CGFloat hdr1Bottom = a1.frame.origin.y + a1.frame.size.height + 16; + + // 右侧装饰图(16x42,15pt在卡片内,27pt在内容区,加到scrollView避免被卡片裁切) + CGFloat decoY = card.frame.origin.y + hdr1Bottom - 15; + UIImageView *decoImg = [[UIImageView alloc] initWithFrame:CGRectMake(kCardLR + connX, decoY, connW, connH)]; + decoImg.image = [UIImage imageNamed:@"health_medical_deco"]; + decoImg.contentMode = UIViewContentModeScaleAspectFit; + [self.scrollView addSubview:decoImg]; + + // 医疗意见与记录卡片到此结束,高度=填报人底部+16 + [self closeCard:card innerBottom:hdr1Bottom]; + + // 小屋意见内容view —— 卡片底部+12(独立view,不在卡片内) + CGFloat contentY = card.frame.origin.y + card.frame.size.height + 12; + NSString *c1 = @"【健康综述】\n患者张建国,男,52岁,高血压病史5年,目前血压控制不理想(收缩压持续偏高)。BMI 28.5,属于肥胖。空腹血糖偏高(6.8mmol/L),处于糖尿病前期。血脂三项均超标,甘油三酯明显偏高。血尿酸持续偏高,存在痛风风险。同型半胱氨酸偏高,心血管风险增加。\n\n【降控目标】\n1. 血压:收缩压控制在140mmHg以下,舒张压控制在90mmHg以下\n2. 体重:目标减至75kg,BMI降至25.3\n3. 血糖:空腹血糖控制在6.1mmol/L以下\n4. 血脂:LDL-C降至3.4以下,TG降至1.7以下\n5. 尿酸:控制在420μmol/L以下\n\n【生活干预建议】\n1. 饮食:低盐低脂低嘌呤饮食,减少动物内脏和海鲜摄入,每日盐摄入不超过5g\n2. 运动:每周至少150分钟中等强度有氧运动(快走、游泳),逐步增加运动量\n3. 戒烟限酒:建议完全戒烟,严格限制酒精摄入\n4. 体重管理:每月减重1-2kg为宜,避免节食减肥\n\n【服药情况】\n1. 氨氯地平片 5mg 每日一次(降压)\n2. 阿托伐他汀钙片 20mg 每晚一次(调脂)\n3. 别嘌醇片 100mg 每日一次(降尿酸)\n4. 叶酸片 0.8mg 每日一次(降低同型半胱氨酸)"; + UIView *ct1 = [self makeOpinionContent:c1]; + ct1.frame = CGRectMake(kCardLR, contentY, kCardW, ct1.frame.size.height); + [self.scrollView addSubview:ct1]; + [self.scrollView bringSubviewToFront:decoImg]; + + // ==================== 专家医生意见 Header ==================== + CGFloat expertY = contentY + ct1.frame.size.height + 12; + UIView *expertCard = [[UIView alloc] initWithFrame:CGRectMake(kCardLR, expertY, kCardW, 0)]; + expertCard.backgroundColor = KWhiteColor; + expertCard.layer.cornerRadius = 12; + expertCard.layer.masksToBounds = YES; + [self.scrollView addSubview:expertCard]; + + CGFloat ei = 16; + + // 居中标题 + UILabel *t2 = [[UILabel alloc] init]; + t2.text = @"专家医生意见"; + t2.textColor = kGreenColor; + t2.font = [UIFont systemFontOfSize:15 weight:UIFontWeightBold]; + [t2 sizeToFit]; + t2.frame = CGRectMake((kCardW - t2.frame.size.width) / 2.0, ei, t2.frame.size.width, 20); + [expertCard addSubview:t2]; + + // 左右装饰线(图片) + CGFloat eLineY = ei + 9; + CGFloat elSpace = t2.frame.origin.x - 27 - kContentLR; + CGFloat erSpace = (kCardW - kContentLR) - (t2.frame.origin.x + t2.frame.size.width + 27); + CGFloat eLineW = MIN(elSpace, erSpace); + if (eLineW > 0) { + UIImageView *ell = [[UIImageView alloc] initWithFrame:CGRectMake(kContentLR, eLineY, eLineW, 1)]; + ell.image = [UIImage imageNamed:@"opinion_line_left"]; + [expertCard addSubview:ell]; + } + if (eLineW > 0) { + CGFloat erx = t2.frame.origin.x + t2.frame.size.width + 27; + UIImageView *erl = [[UIImageView alloc] initWithFrame:CGRectMake(erx, eLineY, eLineW, 1)]; + erl.image = [UIImage imageNamed:@"opinion_line_right"]; + [expertCard addSubview:erl]; + } + + // 填报人 + 时间 + ei = t2.frame.origin.y + t2.frame.size.height + 12; + UILabel *a2 = [[UILabel alloc] initWithFrame:CGRectMake(kContentLR, ei, kCardW - 120, 16)]; + a2.text = @"填报人:陈明远(主任医师)"; + a2.textColor = kGrayTextColor; + a2.font = [UIFont systemFontOfSize:11 weight:UIFontWeightMedium]; + [expertCard addSubview:a2]; + + UILabel *tm2 = [[UILabel alloc] initWithFrame:CGRectMake(kCardW - kContentLR - 160, ei, 160, 16)]; + tm2.text = @"2026-05-18 10:15"; + tm2.textColor = kGrayTextColor; + tm2.font = [UIFont systemFontOfSize:11 weight:UIFontWeightMedium]; + tm2.textAlignment = NSTextAlignmentRight; + [expertCard addSubview:tm2]; + + CGFloat expertHdrBottom = a2.frame.origin.y + a2.frame.size.height + 16; + expertCard.frame = CGRectMake(kCardLR, expertY, kCardW, expertHdrBottom); + + // 装饰图(16x42,15pt在专家卡片内) + CGFloat eDecoY = expertY + expertHdrBottom - 15; + UIImageView *eDecoImg = [[UIImageView alloc] initWithFrame:CGRectMake(kCardLR + kCardW - kContentLR - 16, eDecoY, 16, 42)]; + eDecoImg.image = [UIImage imageNamed:@"health_medical_deco"]; + eDecoImg.contentMode = UIViewContentModeScaleAspectFit; + [self.scrollView addSubview:eDecoImg]; + + // 专家意见内容view —— 卡片底部+12 + CGFloat eContentY = expertY + expertHdrBottom + 12; + NSString *c2 = @"【综合评估】\n患者为代谢综合征高危人群,同时合并高血压、肥胖、高尿酸血症、高同型半胱氨酸血症,心血管事件风险较高。目前空腹血糖6.8mmol/L,建议完善口服葡萄糖耐量试验(OGTT)以明确是否存在糖耐量异常。\n\n【诊疗建议】\n1. 降压方案调整:目前氨氯地平单药控制欠佳,建议联合缬沙坦80mg每日一次,两药联合降压,目标血压130/85mmHg\n2. 调脂治疗:甘油三酯明显偏高(2.9mmol/L),在阿托伐他汀基础上加用非诺贝特200mg每日一次,注意监测肝功能\n3. 高尿酸:继续别嘌醇治疗,建议加用碳酸氢钠片碱化尿液,多饮水(每日>2000ml)\n4. 同型半胱氨酸:继续叶酸补充,建议加用维生素B6和B12联合治疗\n5. 建议3个月后复查全套生化指标,评估治疗效果\n\n【注意事项】\n患者需高度重视生活方式改善,这是所有药物治疗的基础。建议定期(每月一次)到健康小屋随访,监测血压和体重变化。"; + UIView *ct2 = [self makeOpinionContent:c2]; + ct2.frame = CGRectMake(kCardLR, eContentY, kCardW, ct2.frame.size.height); + [self.scrollView addSubview:ct2]; + [self.scrollView bringSubviewToFront:eDecoImg]; + + return eContentY + ct2.frame.size.height + 12; +} + +@end diff --git a/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/XJHomePageV2ViewController.m b/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/XJHomePageV2ViewController.m index dc5f670..6c53530 100644 --- a/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/XJHomePageV2ViewController.m +++ b/XinjiangProject/XinjiangProject/Project/HomeV2/Controllview/XJHomePageV2ViewController.m @@ -32,6 +32,7 @@ #import "XJHomePageV2QuestionTableView.h" #import "MyMessageListViewController.h" #import "EmptyFunctionDevelopmentVC.h" +#import "XJHealthArchiveDetailViewController.h" #import "XJSOSLookDoctoreViewController.h" #import "HomeBannerModel.h" #import "SDCycleScrollView.h" @@ -1121,15 +1122,16 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半 }]; bottomTools.imgeBlock = ^(NSInteger imgIndex) { - - EmptyFunctionDevelopmentVC * vc = [[EmptyFunctionDevelopmentVC alloc] init]; + if (imgIndex == 1) { + EmptyFunctionDevelopmentVC * vc = [[EmptyFunctionDevelopmentVC alloc] init]; vc.navigationItem.title = @"智慧体重"; + [self.navigationController pushViewController:vc animated:YES]; } if (imgIndex == 2) { - vc.navigationItem.title = @"一人一案"; + XJHealthArchiveDetailViewController * vc = [[XJHealthArchiveDetailViewController alloc] init]; + [self.navigationController pushViewController:vc animated:YES]; } - [self.navigationController pushViewController:vc animated:YES]; }; // 健康知识模块 @@ -1145,12 +1147,20 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半 }]; healthKnowledgeView.moreBlock = ^{ + if (![HQCommonUtils isLogin]) { + KPostNotification(KNotificationLoginStateChange, @NO); + return; + } [weakself ensureTokenThenExecute:^{ [weakself openHealthKnowledgeMorePage]; }]; }; healthKnowledgeView.searchBlock = ^(NSString * _Nonnull keyword) { + if (![HQCommonUtils isLogin]) { + KPostNotification(KNotificationLoginStateChange, @NO); + return; + } [weakself ensureTokenThenExecute:^{ [weakself openHealthKnowledgeSearchPage:keyword]; }]; @@ -1180,6 +1190,10 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半 }]; healthNewsView.moreBlock = ^{ + if (![HQCommonUtils isLogin]) { + KPostNotification(KNotificationLoginStateChange, @NO); + return; + } [weakself ensureTokenThenExecute:^{ [weakself openHealthNewsMorePage]; }]; @@ -1298,10 +1312,6 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半 } if (type == 1) { -// EmptyFunctionDevelopmentVC * vc = [[EmptyFunctionDevelopmentVC alloc] init]; -// vc.navigationItem.title = @"健康体检"; -// [self.navigationController pushViewController:vc animated:YES]; - if(![HQCommonUtils isLogin]) { KPostNotification(KNotificationLoginStateChange, @NO); return; @@ -1543,6 +1553,7 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半 NSString * userId = User_ID; if (ValidStr(userId)) { DLog(@"[健康知识] User_ID 为空,跳过获取 token"); + [self.mainScrollView.mj_header endRefreshing]; return; } @@ -1555,6 +1566,7 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半 [weakself loadHealthCategoriesWithToken:t]; } failure:^(NSString * errorMsg) { DLog(@"健康知识 token 获取失败: %@", errorMsg); + [weakself.mainScrollView.mj_header endRefreshing]; }]; } @@ -1576,6 +1588,7 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半 } dispatch_async(dispatch_get_main_queue(), ^{ + BOOL wasLoaded = weakself.healthDataLoaded; weakself.healthDataLoaded = YES; // 健康知识 @@ -1589,13 +1602,25 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半 DLog(@"[健康知识] 标签: %@", titles); weakself.healthKnowledgeView.tabTitles = titles; - weakself.currentHealthCategory = knowledgeCategory.children.firstObject; + // 非首次加载时保留用户选中的标签 + if (wasLoaded) { + NSInteger idx = weakself.healthKnowledgeView.selectedTabIndex; + if (idx >= 0 && idx < knowledgeCategory.children.count) { + weakself.currentHealthCategory = knowledgeCategory.children[idx]; + } else { + weakself.currentHealthCategory = knowledgeCategory.children.firstObject; + } + } else { + weakself.currentHealthCategory = knowledgeCategory.children.firstObject; + } DLog(@"[健康知识] 默认选中: %@ (categoryId=%ld)", weakself.currentHealthCategory.categoryName, (long)weakself.currentHealthCategory.categoryId); [weakself fetchHealthArticlesForCategoryId:weakself.currentHealthCategory.categoryId isNews:NO]; - } else { - DLog(@"[健康知识] 栏目不存在或无子分类"); + } else if (!wasLoaded) { + DLog(@"[健康知识] 栏目不存在或无子分类,首次加载清空"); weakself.healthKnowledgeView.dataArray = @[]; [weakself updateHealthKnowledgeViewHeight]; + } else { + DLog(@"[健康知识] 栏目不存在或无子分类,保留已有数据"); } // 健康资讯:无子分类,直接用 categoryId 查文章 @@ -1603,14 +1628,18 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半 weakself.healthNewsCategoryId = newsCategory.categoryId; DLog(@"[健康资讯] 找到栏目 categoryId=%ld, children=%lu", (long)newsCategory.categoryId, (unsigned long)newsCategory.children.count); [weakself fetchHealthArticlesForCategoryId:newsCategory.categoryId isNews:YES]; - } else { - DLog(@"[健康资讯] 未找到栏目"); + } else if (!wasLoaded) { + DLog(@"[健康资讯] 未找到栏目,首次加载清空"); weakself.healthNewsView.dataArray = @[]; [weakself updateHealthNewsViewHeight]; + } else { + DLog(@"[健康资讯] 未找到栏目,保留已有数据"); } + [weakself.mainScrollView.mj_header endRefreshing]; }); } failure:^(NSString * errorMsg) { DLog(@"[健康知识] 栏目列表获取失败: %@", errorMsg); + [weakself.mainScrollView.mj_header endRefreshing]; }]; } @@ -1623,6 +1652,7 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半 NSString * token = [XJHealthKnowledgeManager sharedManager].cachedToken; if (ValidStr(token)) { DLog(@"[健康知识] fetchArticles token 无效,跳过"); + [weakself.mainScrollView.mj_header endRefreshing]; return; } @@ -1652,6 +1682,12 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半 DLog(@"[%@] 转换后 dataArray 共 %lu 条", tag, (unsigned long)dataArray.count); dispatch_async(dispatch_get_main_queue(), ^{ + // 非首次加载且返回空数据时,不覆盖已有内容 + if (dataArray.count == 0 && weakself.healthDataLoaded) { + DLog(@"[%@] 返回空数据但已加载过,保留已有内容", tag); + [weakself.mainScrollView.mj_header endRefreshing]; + return; + } if (isNews) { weakself.healthNewsView.dataArray = dataArray; [weakself updateHealthNewsViewHeight]; @@ -1659,9 +1695,11 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半 weakself.healthKnowledgeView.dataArray = dataArray; [weakself updateHealthKnowledgeViewHeight]; } + [weakself.mainScrollView.mj_header endRefreshing]; }); } failure:^(NSString * errorMsg) { DLog(@"[%@] 文章列表获取失败: %@", tag, errorMsg); + [weakself.mainScrollView.mj_header endRefreshing]; }]; } @@ -1751,6 +1789,7 @@ static CGFloat const kEmptyTableMinHeight = 250; // 或屏幕高度的一半 } - (void)openHealthKnowledgeDetailPage:(XJHealthKnowledgeModel *)model { + [self showMBProgressHUDOnlyWithString:@"加载中"]; NSString * param = !ValidStr(self.secretString) ? self.secretString : @""; NSString * url = [NSString stringWithFormat:@"%@%@?param=%@&articleId=%@", kHealthKnowledgeH5Host, kHealthDetailH5, param, model.knowledgeId]; diff --git a/XinjiangProject/XinjiangProject/Project/HomeV2/View/BindWatchPopView.m b/XinjiangProject/XinjiangProject/Project/HomeV2/View/BindWatchPopView.m index 72e0e00..c64884f 100644 --- a/XinjiangProject/XinjiangProject/Project/HomeV2/View/BindWatchPopView.m +++ b/XinjiangProject/XinjiangProject/Project/HomeV2/View/BindWatchPopView.m @@ -229,21 +229,25 @@ - (void)updateUI { if (self.type == BindWatchTypeUnbind) { - - [self.actionBtn setTitle:@"解绑" forState:UIControlStateNormal]; + + // 解绑页面:显示已绑定SN,按钮改为"绑定"并禁用(30%透明度,不可点击) + [self.actionBtn setTitle:@"绑定" forState:UIControlStateNormal]; + self.actionBtn.alpha = 0.3; + self.actionBtn.userInteractionEnabled = NO; self.numberField.text = self.serialNumber; self.numberField.backgroundColor = UIColorHex(#F5F7FB); self.numberField.enabled = NO; - - + } else { - + + // 绑定页面:正常可点击 [self.actionBtn setTitle:@"绑定" forState:UIControlStateNormal]; + self.actionBtn.alpha = 1.0; + self.actionBtn.userInteractionEnabled = YES; self.numberField.text = @""; self.numberField.enabled = YES; self.numberField.backgroundColor = KWhiteColor; - } } diff --git a/XinjiangProject/XinjiangProject/Project/HomeV2/View/XJHealthKnowledgeCell.m b/XinjiangProject/XinjiangProject/Project/HomeV2/View/XJHealthKnowledgeCell.m index c2aade3..8e7227f 100644 --- a/XinjiangProject/XinjiangProject/Project/HomeV2/View/XJHealthKnowledgeCell.m +++ b/XinjiangProject/XinjiangProject/Project/HomeV2/View/XJHealthKnowledgeCell.m @@ -146,7 +146,7 @@ self.titleLabel.text = model.title; self.descLabel.text = model.content; - self.timeLabel.text = model.createTime; + self.timeLabel.text = [self dateString:model.createTime]; // 标签 #健康资讯#肝癌 → 健康资讯 | 肝癌 self.tagLabel.text = [self formatTagString:model.tags]; @@ -174,4 +174,11 @@ return result; } +/// 截取日期部分 "2026-04-23 15:28:57" → "2026-04-23",容错处理 +- (NSString *)dateString:(NSString *)datetime { + if (!datetime || ![datetime isKindOfClass:[NSString class]] || datetime.length == 0) return @""; + NSArray *parts = [datetime componentsSeparatedByString:@" "]; + return parts.firstObject ?: datetime; +} + @end diff --git a/XinjiangProject/XinjiangProject/Project/HomeV2/View/XJHealthKnowledgeView.m b/XinjiangProject/XinjiangProject/Project/HomeV2/View/XJHealthKnowledgeView.m index 0ef7f5d..fb9d6c5 100644 --- a/XinjiangProject/XinjiangProject/Project/HomeV2/View/XJHealthKnowledgeView.m +++ b/XinjiangProject/XinjiangProject/Project/HomeV2/View/XJHealthKnowledgeView.m @@ -197,7 +197,7 @@ static CGFloat const kTabAreaHeight = 48; // 空状态 - 放在 tabScrollView 下方 self.emptyLabel = [[UILabel alloc] init]; self.emptyLabel.text = @"暂无健康知识"; - self.emptyLabel.textColor = [UIColor colorWithRed:0.6 green:0.6 blue:0.6 alpha:1.0]; + self.emptyLabel.textColor = UIColorHex(#14BEBE); self.emptyLabel.font = [UIFont systemFontOfSize:15]; self.emptyLabel.textAlignment = NSTextAlignmentCenter; self.emptyLabel.hidden = YES; @@ -315,6 +315,7 @@ static CGFloat const kTabAreaHeight = 48; self.dataArray = @[]; self.tabTitles = nil; self.emptyLabel.text = @"暂未登录"; + self.emptyLabel.textColor = [UIColor colorWithRed:0.6 green:0.6 blue:0.6 alpha:1.0]; self.emptyLabel.hidden = NO; // 未登录时在头部下方区域上下居中 [self.emptyLabel mas_remakeConstraints:^(MASConstraintMaker *make) { diff --git a/XinjiangProject/XinjiangProject/Project/HomeV2/View/XJHealthNewsView.m b/XinjiangProject/XinjiangProject/Project/HomeV2/View/XJHealthNewsView.m index cd6bf4a..98c2121 100644 --- a/XinjiangProject/XinjiangProject/Project/HomeV2/View/XJHealthNewsView.m +++ b/XinjiangProject/XinjiangProject/Project/HomeV2/View/XJHealthNewsView.m @@ -109,7 +109,7 @@ static CGFloat const kDefaultEmptyHeight = 130; // 空状态 self.emptyLabel = [[UILabel alloc] init]; self.emptyLabel.text = @"暂无健康资讯"; - self.emptyLabel.textColor = [UIColor colorWithRed:0.6 green:0.6 blue:0.6 alpha:1.0]; + self.emptyLabel.textColor = UIColorHex(#14BEBE); self.emptyLabel.font = [UIFont systemFontOfSize:15]; self.emptyLabel.textAlignment = NSTextAlignmentCenter; self.emptyLabel.hidden = YES; @@ -150,6 +150,7 @@ static CGFloat const kDefaultEmptyHeight = 130; - (void)showNotLoggedInPlaceholder { self.dataArray = @[]; self.emptyLabel.text = @"暂未登录"; + self.emptyLabel.textColor = [UIColor colorWithRed:0.6 green:0.6 blue:0.6 alpha:1.0]; self.emptyLabel.hidden = NO; [self.emptyLabel mas_remakeConstraints:^(MASConstraintMaker *make) { make.centerX.mas_equalTo(self); diff --git a/XinjiangProject/XinjiangProject/Project/HomeV2/View/XJHomePageV2HeardView.m b/XinjiangProject/XinjiangProject/Project/HomeV2/View/XJHomePageV2HeardView.m index 5b1ceea..895d831 100644 --- a/XinjiangProject/XinjiangProject/Project/HomeV2/View/XJHomePageV2HeardView.m +++ b/XinjiangProject/XinjiangProject/Project/HomeV2/View/XJHomePageV2HeardView.m @@ -254,7 +254,7 @@ SDCycleScrollView * SDCycles = [SDCycleScrollView cycleScrollViewWithFrame:CGRectZero delegate:self placeholderImage: [UIImage imageNamed:@"placeholder"]]; SDCycles.layer.cornerRadius = 12; SDCycles.layer.masksToBounds = true; - SDCycles.backgroundColor = KClearColor; + SDCycles.backgroundColor = KWhiteColor; // UIViewContentModeScaleToFill, // UIViewContentModeScaleAspectFit, // contents scaled to fit with fixed aspect. remainder is transparent // UIViewContentModeScaleAspectFill, @@ -355,7 +355,7 @@ SDCycleScrollView * questionSDCycles = [SDCycleScrollView cycleScrollViewWithFrame:CGRectZero delegate:self placeholderImage:[UIImage imageNamed:@"placeholder"]]; - questionSDCycles.backgroundColor = KClearColor; + questionSDCycles.backgroundColor = KWhiteColor; questionSDCycles.layer.cornerRadius = 12; questionSDCycles.layer.masksToBounds = true; questionSDCycles.bannerImageViewContentMode = UIViewContentModeScaleToFill; diff --git a/XinjiangProject/XinjiangProject/Project/Qusetion/Controller/BannarDetailViewController.m b/XinjiangProject/XinjiangProject/Project/Qusetion/Controller/BannarDetailViewController.m index bcf1e71..c541a69 100644 --- a/XinjiangProject/XinjiangProject/Project/Qusetion/Controller/BannarDetailViewController.m +++ b/XinjiangProject/XinjiangProject/Project/Qusetion/Controller/BannarDetailViewController.m @@ -63,6 +63,7 @@ // detailImg.image = [UIImage imageNamed:@"ADDetail"]; self.detailImg = detailImg; self.detailImg.sd_imageIndicator = [SDWebImageActivityIndicator grayIndicator]; + self.detailImg.contentMode = UIViewContentModeScaleAspectFit; [contentView addSubview:detailImg]; @@ -109,7 +110,8 @@ photo = [ photo stringByReplacingOccurrencesOfString:str1 withString:@"/"]; NSString *encodedString3 = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef) photo, (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL, kCFStringEncodingUTF8)); - + + [[SDImageCache sharedImageCache] removeImageForKey:encodedString3 withCompletion:nil]; [self.detailImg sd_setImageWithURL:[NSURL URLWithString:encodedString3] placeholderImage:nil options:SDWebImageRetryFailed @@ -118,8 +120,14 @@ SDImageCacheType cacheType, NSURL * _Nullable imageURL) { - if (error) { - self.detailImg.image = [UIImage imageNamed:@"image_load_fail"]; // 失败图 + if (!error && image) { + CGFloat ratio = image.size.height / image.size.width; + CGFloat imgHeight = kScreenWidth * ratio; + [self.detailImg mas_updateConstraints:^(MASConstraintMaker *make) { + make.height.mas_equalTo(imgHeight); + }]; + } else if (error) { + self.detailImg.image = [UIImage imageNamed:@"image_load_fail"]; } }]; diff --git a/XinjiangProject/XinjiangProject/Project/SOS/Controller/XJFirstAidResourceDetailVC.h b/XinjiangProject/XinjiangProject/Project/SOS/Controller/XJFirstAidResourceDetailVC.h new file mode 100644 index 0000000..2f12303 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Project/SOS/Controller/XJFirstAidResourceDetailVC.h @@ -0,0 +1,32 @@ +// +// XJFirstAidResourceDetailVC.h +// XinjiangProject +// +// 急救宣教资源详情页 +// ┌──────────────────────────────┐ +// │ NavigationBar │ +// ├──────────────────────────────┤ +// │ UIScrollView │ +// │ ┌────────────────────────┐ │ +// │ │ 封面图 (有则展示) │ │ +// │ ├────────────────────────┤ │ +// │ │ 标题 │ │ +// │ ├────────────────────────┤ │ +// │ │ 分类 · 浏览 · 时间 │ │ +// │ ├────────────────────────┤ │ +// │ │ WKWebView 富文本正文 │ │ +// │ └────────────────────────┘ │ +// └──────────────────────────────┘ +// + +#import "XJBaseViewController.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface XJFirstAidResourceDetailVC : XJBaseViewController + +@property (nonatomic, copy) NSString * resourceId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/XinjiangProject/XinjiangProject/Project/SOS/Controller/XJFirstAidResourceDetailVC.m b/XinjiangProject/XinjiangProject/Project/SOS/Controller/XJFirstAidResourceDetailVC.m new file mode 100644 index 0000000..97c1db1 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Project/SOS/Controller/XJFirstAidResourceDetailVC.m @@ -0,0 +1,325 @@ +// +// XJFirstAidResourceDetailVC.m +// XinjiangProject +// + +#import "XJFirstAidResourceDetailVC.h" +#import "XJFirstAidResourceModel.h" +#import +#import + +@interface XJFirstAidResourceDetailVC () + +@property (nonatomic, strong) UIScrollView * scrollView; +@property (nonatomic, strong) UIView * contentView; + +// 内容区 +@property (nonatomic, strong) UIImageView * coverImageView; +@property (nonatomic, strong) UILabel * titleLabel; +@property (nonatomic, strong) UILabel * infoLabel; // 分类 · 浏览 · 时间 +@property (nonatomic, strong) UIView * infoSeparator; +@property (nonatomic, strong) WKWebView * webView; + +// 约束(动态更新 WebView 高度) +@property (nonatomic, strong) MASConstraint * webViewHeightConstraint; + +// 数据 +@property (nonatomic, strong) XJFirstAidResourceModel * model; + +@end + +@implementation XJFirstAidResourceDetailVC + +#pragma mark - ————— Life Cycle ————— + +- (void)viewDidLoad { + [super viewDidLoad]; + self.view.backgroundColor = CViewBgColor; + self.title = @"文章详情"; + + [self createUI]; + [self loadDetailData]; +} + +#pragma mark - ————— UI 搭建 ————— + +- (void)createUI { + // 滚动视图 + self.scrollView = [[UIScrollView alloc] init]; + self.scrollView.showsVerticalScrollIndicator = NO; + self.scrollView.backgroundColor = CViewBgColor; + [self.view addSubview:self.scrollView]; + [self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.mas_equalTo(self.view); + }]; + + // 内容容器 + self.contentView = [[UIView alloc] init]; + self.contentView.backgroundColor = CViewBgColor; + [self.scrollView addSubview:self.contentView]; + [self.contentView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.mas_equalTo(self.scrollView); + make.width.mas_equalTo(self.scrollView); + }]; + + // 白色卡片 + UIView * cardView = [[UIView alloc] init]; + cardView.backgroundColor = KWhiteColor; + cardView.layer.cornerRadius = 10; + cardView.layer.masksToBounds = YES; + [self.contentView addSubview:cardView]; + [cardView mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.mas_offset(12); + make.left.mas_offset(12); + make.right.mas_offset(-12); + make.bottom.mas_equalTo(self.contentView).offset(-20); + }]; + + // 封面图 + self.coverImageView = [[UIImageView alloc] init]; + self.coverImageView.contentMode = UIViewContentModeScaleAspectFill; + self.coverImageView.layer.masksToBounds = YES; + self.coverImageView.hidden = YES; + [cardView addSubview:self.coverImageView]; + [self.coverImageView mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.left.right.mas_offset(0); + make.height.mas_offset(200); + }]; + + // 标题 + self.titleLabel = [[UILabel alloc] init]; + self.titleLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightBold]; + self.titleLabel.textColor = UIColorHex(0x1A1A1A); + self.titleLabel.numberOfLines = 0; + [cardView addSubview:self.titleLabel]; + [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.mas_equalTo(self.coverImageView.mas_bottom).offset(16); + make.left.mas_offset(16); + make.right.mas_offset(-16); + }]; + + // 信息栏(分类 · 浏览 · 时间) + self.infoLabel = [[UILabel alloc] init]; + self.infoLabel.font = [UIFont systemFontOfSize:12 weight:UIFontWeightRegular]; + self.infoLabel.textColor = UIColorHex(0xB0B0B0); + [cardView addSubview:self.infoLabel]; + [self.infoLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.mas_equalTo(self.titleLabel.mas_bottom).offset(10); + make.left.mas_offset(16); + make.right.mas_offset(-16); + }]; + + // 分割线 + self.infoSeparator = [[UIView alloc] init]; + self.infoSeparator.backgroundColor = UIColorHex(0xEAECF1); + [cardView addSubview:self.infoSeparator]; + [self.infoSeparator mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.mas_equalTo(self.infoLabel.mas_bottom).offset(14); + make.left.mas_offset(16); + make.right.mas_offset(-16); + make.height.mas_offset(1); + }]; + + // WebView 渲染富文本正文 + self.webView = [[WKWebView alloc] init]; + self.webView.navigationDelegate = self; + self.webView.scrollView.scrollEnabled = NO; + self.webView.opaque = NO; + self.webView.backgroundColor = KWhiteColor; + [cardView addSubview:self.webView]; + [self.webView mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.mas_equalTo(self.infoSeparator.mas_bottom).offset(14); + make.left.mas_offset(12); + make.right.mas_offset(-12); + self.webViewHeightConstraint = make.height.mas_offset(1); + make.bottom.mas_equalTo(cardView).offset(-20); + }]; +} + +#pragma mark - ————— 网络请求 ————— + +- (void)loadDetailData { + [self showMBProgressHUDWithString:@"加载中..." autoHidden:NO]; + + NSString * urlStr = [NSString stringWithFormat:@"%@%@?id=%@", Http, kFirstAidResourceDetailPath, self.resourceId]; + + NSMutableURLRequest * request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlStr]]; + request.HTTPMethod = @"GET"; + [request setValue:@"application/json; charset=utf-8" forHTTPHeaderField:@"Content-Type"]; + + // 获取 token + HQUserInfo * user = [HQCommonUtils keyedUnarchiverWithKey:LoginUserInfo]; + NSString * accessToken = user.token ?: @""; + if (accessToken.length > 0) { + [request setValue:accessToken forHTTPHeaderField:@"X-Access-Token"]; + } + + NSURLSession * session = [NSURLSession sharedSession]; + kWeakSelf(self); + NSURLSessionDataTask * task = [session dataTaskWithRequest:request + completionHandler:^(NSData * _Nullable data, + NSURLResponse * _Nullable response, + NSError * _Nullable error) { + dispatch_async(dispatch_get_main_queue(), ^{ + [weakself handleDetailResponse:data error:error response:response]; + }); + }]; + [task resume]; +} + +- (void)handleDetailResponse:(NSData *)data error:(NSError *)error response:(NSURLResponse *)response { + [self hiddenAllMBProgressHUD]; + + if (error) { + [EasyTextView showErrorText:@"网络异常,请稍后重试"]; + return; + } + + NSError * jsonError = nil; + NSDictionary * json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; + if (jsonError || ![json isKindOfClass:[NSDictionary class]]) { + [EasyTextView showErrorText:@"数据解析失败"]; + return; + } + + NSDictionary * result = json[@"result"]; + if (![result isKindOfClass:[NSDictionary class]]) { + [EasyTextView showErrorText:@"数据为空"]; + return; + } + + XJFirstAidResourceModel * model = [[XJFirstAidResourceModel alloc] init]; + model.resourceId = [NSString stringWithFormat:@"%@", result[@"id"] ?: @""]; + model.title = [NSString stringWithFormat:@"%@", result[@"title"] ?: @""]; + model.summary = [NSString stringWithFormat:@"%@", result[@"summary"] ?: @""]; + model.content = [NSString stringWithFormat:@"%@", result[@"content"] ?: @""]; + model.coverImage = [NSString stringWithFormat:@"%@", result[@"coverImage"] ?: @""]; + model.contentType = [NSString stringWithFormat:@"%@", result[@"contentType"] ?: @""]; + model.categoryName = [NSString stringWithFormat:@"%@", result[@"categoryName"] ?: @""]; + model.tags = [NSString stringWithFormat:@"%@", result[@"tags"] ?: @""]; + model.viewCount = [result[@"viewCount"] integerValue]; + model.favoriteCount = [result[@"favoriteCount"] integerValue]; + model.isFavorited = [result[@"isFavorited"] boolValue]; + model.createTime = [NSString stringWithFormat:@"%@", result[@"createTime"] ?: @""]; + model.fileUrl = [NSString stringWithFormat:@"%@", result[@"fileUrl"] ?: @""]; + + NSArray * tagList = result[@"tagNameList"]; + if ([tagList isKindOfClass:[NSArray class]]) { + model.tagNameList = tagList; + } else { + model.tagNameList = @[]; + } + + self.model = model; + [self renderContent]; +} + +#pragma mark - ————— 渲染内容 ————— + +- (void)renderContent { + self.title = self.model.title; + + // 封面图 + if (self.model.coverImage.length > 0 && ![self.model.coverImage isEqualToString:@"(null)"]) { + self.coverImageView.hidden = NO; + NSString * encoded = [self encodeURL:self.model.coverImage]; + [self.coverImageView sd_setImageWithURL:[NSURL URLWithString:encoded] placeholderImage:nil]; + [self.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) { + make.top.mas_equalTo(self.coverImageView.mas_bottom).offset(16); + make.left.mas_offset(16); + make.right.mas_offset(-16); + }]; + } else { + self.coverImageView.hidden = YES; + [self.coverImageView mas_remakeConstraints:^(MASConstraintMaker *make) { + make.top.left.right.mas_offset(0); + make.height.mas_offset(0); + }]; + [self.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) { + make.top.mas_offset(16); + make.left.mas_offset(16); + make.right.mas_offset(-16); + }]; + } + + // 标题 + self.titleLabel.text = self.model.title; + + // 信息栏 + NSString * infoText = [NSString stringWithFormat:@"%@", self.model.categoryName]; + if (self.model.viewCount > 0) { + infoText = [infoText stringByAppendingFormat:@" · %ld次浏览", (long)self.model.viewCount]; + } + if (self.model.createTime.length > 0) { + NSString * timeStr = self.model.createTime; + if (timeStr.length >= 10) { + timeStr = [timeStr substringToIndex:10]; + } + infoText = [infoText stringByAppendingFormat:@" · %@", timeStr]; + } + self.infoLabel.text = infoText; + + // 加载富文本 + if (self.model.content.length > 0) { + NSString * htmlString = [self buildHTMLString:self.model.content]; + [self.webView loadHTMLString:htmlString baseURL:nil]; + } else { + [self updateWebViewHeight:0]; + } +} + +#pragma mark - ————— HTML 拼接 ————— + +- (NSString *)buildHTMLString:(NSString *)bodyContent { + NSString * html = [NSString stringWithFormat:@"" + "" + "" + "" + "%@", + bodyContent]; + return html; +} + +#pragma mark - ————— WKNavigationDelegate ————— + +- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { + // 加载完成后获取内容实际高度 + kWeakSelf(self); + [webView evaluateJavaScript:@"document.body.scrollHeight" completionHandler:^(id result, NSError * _Nullable error) { + CGFloat height = [result floatValue]; + dispatch_async(dispatch_get_main_queue(), ^{ + [weakself updateWebViewHeight:height]; + }); + }]; +} + +- (void)updateWebViewHeight:(CGFloat)height { + if (height <= 0) { + height = 1; + } + [self.webViewHeightConstraint uninstall]; + [self.webView mas_updateConstraints:^(MASConstraintMaker *make) { + self.webViewHeightConstraint = make.height.mas_offset(height); + }]; +} + +#pragma mark - ————— URL 编码 ————— + +- (NSString *)encodeURL:(NSString *)url { + if (![url hasPrefix:@"http"]) { + url = [NSString stringWithFormat:@"%@%@", ResourceAddress, url]; + } + url = [url stringByReplacingOccurrencesOfString:@"\\" withString:@"/"]; + return [url stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]] ?: url; +} + +#pragma mark - ————— Setter / Getter ————— + + + +@end diff --git a/XinjiangProject/XinjiangProject/Project/SOS/Controller/XJSelectContactViewController.h b/XinjiangProject/XinjiangProject/Project/SOS/Controller/XJSelectContactViewController.h new file mode 100644 index 0000000..4d9ab93 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Project/SOS/Controller/XJSelectContactViewController.h @@ -0,0 +1,17 @@ +// +// XJSelectContactViewController.h +// XinjiangProject +// + +#import "XJBaseViewController.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface XJSelectContactViewController : XJBaseViewController + +@property (nonatomic, copy) NSString *chatID; +@property (nonatomic, copy) NSString *groupId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/XinjiangProject/XinjiangProject/Project/SOS/Controller/XJSelectContactViewController.m b/XinjiangProject/XinjiangProject/Project/SOS/Controller/XJSelectContactViewController.m new file mode 100644 index 0000000..a16b3b2 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Project/SOS/Controller/XJSelectContactViewController.m @@ -0,0 +1,385 @@ +// +// XJSelectContactViewController.m +// XinjiangProject +// + +#import "XJSelectContactViewController.h" +#import "XJPNetAPI.h" + +typedef NS_ENUM(NSUInteger, XJContactTab) { + XJContactTabEmployee = 0, + XJContactTabExpert = 1, +}; + +@interface XJSelectContactViewController () + +@property (nonatomic, assign) XJContactTab currentTab; + +// 顶部切换 +@property (nonatomic, strong) UIView *tabBgView; +@property (nonatomic, strong) UILabel *employeeTabLabel; +@property (nonatomic, strong) UILabel *expertTabLabel; + +// 搜索 +@property (nonatomic, strong) UITextField *searchField; + +// 列表 +@property (nonatomic, strong) UITableView *tableView; +@property (nonatomic, strong) NSMutableArray *dataArray; + +// 选中集合 +@property (nonatomic, strong) NSMutableSet *selectedIds; + +// 底部 +@property (nonatomic, strong) UIView *bottomView; + +@end + +@implementation XJSelectContactViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + self.isHidenNaviBar = NO; + self.navigationItem.title = @"选择联系人"; + self.view.backgroundColor = CViewBgColor; + + self.currentTab = XJContactTabEmployee; + self.dataArray = [NSMutableArray array]; + self.selectedIds = [NSMutableSet set]; + + [self createTopView]; + [self createBottomView]; + [self createTableView]; + [self loadData]; +} + +- (void)viewDidLayoutSubviews { + [super viewDidLayoutSubviews]; + + CGFloat safeBottom = 0; + if (@available(iOS 11.0, *)) { + safeBottom = self.view.safeAreaInsets.bottom; + } + CGFloat bottomH = 60 + safeBottom; + self.bottomView.frame = CGRectMake(0, self.view.bounds.size.height - bottomH, kScreenWidth, bottomH); + + CGFloat top = 94; + CGFloat tableBottom = self.bottomView.frame.origin.y - 15; + self.tableView.frame = CGRectMake(0, top, kScreenWidth, tableBottom - top); +} + +#pragma mark - 顶部区域 (高度94) + +- (void)createTopView { + UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 94)]; + container.backgroundColor = KWhiteColor; + [self.view addSubview:container]; + + // 搜索按钮(右侧) + UIButton *searchBtn = [[UIButton alloc] initWithFrame:CGRectMake(kScreenWidth - 56, 6, 56, 32)]; + [searchBtn setTitle:@"搜索" forState:UIControlStateNormal]; + searchBtn.titleLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium]; + [searchBtn setTitleColor:UIColorHex(0x2EB8B2) forState:UIControlStateNormal]; + [searchBtn addTarget:self action:@selector(didTapSearch) forControlEvents:UIControlEventTouchUpInside]; + [container addSubview:searchBtn]; + + // 搜索框 + UIView *searchBg = [[UIView alloc] initWithFrame:CGRectMake(15, 6, kScreenWidth - 15 - 56, 32)]; + searchBg.backgroundColor = UIColorHex(0xF6F6F6); + searchBg.layer.cornerRadius = 8; + searchBg.layer.masksToBounds = YES; + [container addSubview:searchBg]; + + UIImageView *searchIcon = [[UIImageView alloc] initWithFrame:CGRectMake(12, (32 - 14) / 2, 14, 14)]; + searchIcon.image = [UIImage imageNamed:@"searchLeftIcon"]; + searchIcon.contentMode = UIViewContentModeScaleAspectFit; + [searchBg addSubview:searchIcon]; + + self.searchField = [[UITextField alloc] initWithFrame:CGRectMake(34, 0, searchBg.bounds.size.width - 34, 32)]; + self.searchField.font = [UIFont systemFontOfSize:14]; + self.searchField.returnKeyType = UIReturnKeySearch; + self.searchField.placeholder = @"请输入姓名搜索"; + [self.searchField addTarget:self action:@selector(didTapSearch) forControlEvents:UIControlEventEditingDidEndOnExit]; + [searchBg addSubview:self.searchField]; + + // 切换标签(搜索框底部 + 10) + CGFloat tabY = 6 + 32 + 10; + self.tabBgView = [[UIView alloc] initWithFrame:CGRectMake(17, tabY, kScreenWidth - 34, 36)]; + self.tabBgView.backgroundColor = UIColorHex(0xE8EBF2); + self.tabBgView.layer.cornerRadius = 8; + self.tabBgView.layer.masksToBounds = YES; + [container addSubview:self.tabBgView]; + + CGFloat tabW = (self.tabBgView.bounds.size.width - 4) / 2; + CGFloat tabH = 32; + + UIView *employeeBg = [[UIView alloc] initWithFrame:CGRectMake(2, 2, tabW, tabH)]; + employeeBg.backgroundColor = KWhiteColor; + employeeBg.layer.cornerRadius = 8; + employeeBg.layer.masksToBounds = YES; + employeeBg.tag = 200; + [self.tabBgView addSubview:employeeBg]; + + self.employeeTabLabel = [[UILabel alloc] initWithFrame:employeeBg.bounds]; + self.employeeTabLabel.text = @"邀请员工"; + self.employeeTabLabel.textAlignment = NSTextAlignmentCenter; + self.employeeTabLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium]; + self.employeeTabLabel.textColor = UIColorHex(0x2EB8B2); + self.employeeTabLabel.userInteractionEnabled = YES; + [employeeBg addSubview:self.employeeTabLabel]; + [employeeBg addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapEmployeeTab)]]; + + UIView *expertBg = [[UIView alloc] initWithFrame:CGRectMake(2 + tabW, 2, tabW, tabH)]; + expertBg.backgroundColor = UIColorHex(0xE8EBF2); + expertBg.layer.cornerRadius = 8; + expertBg.layer.masksToBounds = YES; + expertBg.tag = 201; + [self.tabBgView addSubview:expertBg]; + + self.expertTabLabel = [[UILabel alloc] initWithFrame:expertBg.bounds]; + self.expertTabLabel.text = @"邀请专家"; + self.expertTabLabel.textAlignment = NSTextAlignmentCenter; + self.expertTabLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightRegular]; + self.expertTabLabel.textColor = UIColorHex(0x808080); + self.expertTabLabel.userInteractionEnabled = YES; + [expertBg addSubview:self.expertTabLabel]; + [expertBg addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapExpertTab)]]; + + // 头部分割线 + UIView *headerLine = [[UIView alloc] initWithFrame:CGRectMake(0, 93, kScreenWidth, 1)]; + headerLine.backgroundColor = UIColorHex(0xEAECF1); + [container addSubview:headerLine]; +} + +#pragma mark - 底部区域 + +- (void)createBottomView { + self.bottomView = [[UIView alloc] initWithFrame:CGRectZero]; + self.bottomView.backgroundColor = KWhiteColor; + [self.view addSubview:self.bottomView]; + + UIButton *confirmBtn = [[UIButton alloc] initWithFrame:CGRectMake(45, 8, kScreenWidth - 90, 44)]; + confirmBtn.backgroundColor = UIColorHex(0x2EB8B1); + confirmBtn.layer.cornerRadius = 12; + confirmBtn.layer.masksToBounds = YES; + [confirmBtn setTitle:@"确定" forState:UIControlStateNormal]; + [confirmBtn setTitleColor:UIColorHex(0xFFFFFF) forState:UIControlStateNormal]; + confirmBtn.titleLabel.font = [UIFont boldSystemFontOfSize:18]; + [confirmBtn addTarget:self action:@selector(didTapConfirm) forControlEvents:UIControlEventTouchUpInside]; + [self.bottomView addSubview:confirmBtn]; +} + +#pragma mark - 列表 + +- (void)createTableView { + self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; + self.tableView.delegate = self; + self.tableView.dataSource = self; + self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + self.tableView.rowHeight = 70; + self.tableView.backgroundColor = CViewBgColor; + + LYEmptyView *emptyView = [LYEmptyView emptyViewWithImageStr:@"empty_placeholder" titleStr:@"暂无数据" detailStr:@""]; + emptyView.subViewMargin = 20.f; + emptyView.titleLabTextColor = CFontColor1; + emptyView.detailLabFont = [UIFont systemFontOfSize:17]; + self.tableView.ly_emptyView = emptyView; + + [self.view addSubview:self.tableView]; +} + +- (void)loadData { + [self showMBProgressHUDWithString:@"加载中..." autoHidden:NO]; + [self.dataArray removeAllObjects]; + [self.tableView reloadData]; + + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (self.groupId.length > 0) { + [params setObject:self.groupId forKey:@"excludeSessionId"]; + } + NSString *keyword = self.searchField.text; + if (keyword.length > 0) { + [params setObject:keyword forKey:@"realname"]; + } + [params setObject:@(1) forKey:@"pageNo"]; + [params setObject:@(200) forKey:@"pageSize"]; + + NSString *tag = (self.currentTab == XJContactTabEmployee) ? @"SEARCH_EMPLOYEE" : @"SEARCH_EXPERT"; + XJPNetAPI *api = [[XJPNetAPI alloc] init:self tag:tag NeedToken:@""]; + + if (self.currentTab == XJContactTabEmployee) { + [api getSearchEmployee:params]; + } else { + [api getSearchExpert:params]; + } +} + +- (void)didTapSearch { + [self loadData]; +} + +- (void)didTapConfirm { + if (self.selectedIds.count == 0) { + [EasyTextView showErrorText:@"请先选择联系人"]; + return; + } + [self showMBProgressHUDWithString:@"邀请中..." autoHidden:NO]; + + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + [params setObject:self.groupId forKey:@"groupId"]; + [params setObject:[self.selectedIds allObjects] forKey:@"memberList"]; + NSInteger memberType = (self.currentTab == XJContactTabEmployee) ? 1 : 0; + [params setObject:@(memberType) forKey:@"memberType"]; + + XJPNetAPI *api = [[XJPNetAPI alloc] init:self tag:@"ADDGROUPUSER" NeedToken:@""]; + [api postAddGroupUser:params]; +} + +#pragma mark - 切换 + +- (void)didTapEmployeeTab { + if (self.currentTab == XJContactTabEmployee) return; + self.currentTab = XJContactTabEmployee; + [self updateTabStyle]; + [self loadData]; +} + +- (void)didTapExpertTab { + if (self.currentTab == XJContactTabExpert) return; + self.currentTab = XJContactTabExpert; + [self updateTabStyle]; + [self loadData]; +} + +- (void)updateTabStyle { + UIView *employeeBg = [self.tabBgView viewWithTag:200]; + UIView *expertBg = [self.tabBgView viewWithTag:201]; + + if (self.currentTab == XJContactTabEmployee) { + employeeBg.backgroundColor = KWhiteColor; + self.employeeTabLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium]; + self.employeeTabLabel.textColor = UIColorHex(0x2EB8B2); + expertBg.backgroundColor = UIColorHex(0xE8EBF2); + self.expertTabLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightRegular]; + self.expertTabLabel.textColor = UIColorHex(0x808080); + } else { + expertBg.backgroundColor = KWhiteColor; + self.expertTabLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium]; + self.expertTabLabel.textColor = UIColorHex(0x2EB8B2); + employeeBg.backgroundColor = UIColorHex(0xE8EBF2); + self.employeeTabLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightRegular]; + self.employeeTabLabel.textColor = UIColorHex(0x808080); + } +} + +#pragma mark - UITableView + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return self.dataArray.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + static NSString *cellId = @"SelectContactCell"; + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId]; + if (!cell) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId]; + cell.selectionStyle = UITableViewCellSelectionStyleNone; + cell.backgroundColor = KWhiteColor; + + UIImageView *avatar = [[UIImageView alloc] initWithFrame:CGRectMake(15, 12, 45, 45)]; + avatar.tag = 100; + avatar.layer.cornerRadius = 12; + avatar.layer.masksToBounds = YES; + [cell.contentView addSubview:avatar]; + + UILabel *nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(72, 17, kScreenWidth - 72 - 51, 18)]; + nameLabel.tag = 101; + nameLabel.font = [UIFont systemFontOfSize:15 weight:UIFontWeightMedium]; + nameLabel.textColor = UIColorHex(0x000000); + [cell.contentView addSubview:nameLabel]; + + UILabel *deptLabel = [[UILabel alloc] initWithFrame:CGRectMake(72, 39, kScreenWidth - 72 - 51, 14)]; + deptLabel.tag = 102; + deptLabel.font = [UIFont systemFontOfSize:12 weight:UIFontWeightRegular]; + deptLabel.textColor = UIColorHex(0x808080); + [cell.contentView addSubview:deptLabel]; + + UIButton *checkBtn = [[UIButton alloc] initWithFrame:CGRectMake(kScreenWidth - 36, (70 - 21) / 2, 21, 21)]; + checkBtn.tag = 103; + checkBtn.userInteractionEnabled = NO; + [cell.contentView addSubview:checkBtn]; + + UIView *line = [[UIView alloc] initWithFrame:CGRectMake(72, 70 - 1, kScreenWidth - 72, 1)]; + line.tag = 104; + line.backgroundColor = UIColorHex(0xEAECF1); + [cell.contentView addSubview:line]; + } + + NSDictionary *contact = self.dataArray[indexPath.row]; + UIImageView *avatar = [cell.contentView viewWithTag:100]; + UILabel *nameLabel = [cell.contentView viewWithTag:101]; + UILabel *deptLabel = [cell.contentView viewWithTag:102]; + UIButton *checkBtn = [cell.contentView viewWithTag:103]; + + nameLabel.text = contact[@"realname"] ?: @""; + + if (self.currentTab == XJContactTabEmployee) { + deptLabel.text = [NSString stringWithFormat:@"%@-%@", contact[@"orgName"] ?: @"", contact[@"deptName"] ?: @""]; + } else { + deptLabel.text = contact[@"post_dictText"] ?: @""; + } + + NSString *avatarPath = contact[@"avatar"]; + if (avatarPath.length > 0) { + NSString *fullUrl = [avatarPath hasPrefix:@"http"] ? avatarPath : [NSString stringWithFormat:@"%@%@", ResourceAddress, avatarPath]; + [avatar sd_setImageWithURL:[NSURL URLWithString:fullUrl] placeholderImage:[UIImage imageNamed:@"heard_man"]]; + } else { + avatar.image = [UIImage imageNamed:@"heard_man"]; + } + + NSString *userId = contact[@"userId"] ?: @""; + BOOL selected = [self.selectedIds containsObject:userId]; + NSString *iconName = selected ? @"firstAid_checkbox_selected" : @"firstAid_checkbox_unselected"; + [checkBtn setImage:[UIImage imageNamed:iconName] forState:UIControlStateNormal]; + + return cell; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + [tableView deselectRowAtIndexPath:indexPath animated:YES]; + + NSDictionary *contact = self.dataArray[indexPath.row]; + NSString *userId = contact[@"userId"] ?: @""; + if ([self.selectedIds containsObject:userId]) { + [self.selectedIds removeObject:userId]; + } else { + [self.selectedIds addObject:userId]; + } + [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; +} + +#pragma mark - Network + +- (void)Failed:(NSString *)message tag:(NSString *)tag { + [self hiddenAllMBProgressHUD]; + [EasyTextView showErrorText:message]; +} + +- (void)Sucess:(id)response tag:(NSString *)tag { + [self hiddenAllMBProgressHUD]; + if ([tag isEqualToString:@"SEARCH_EMPLOYEE"] || [tag isEqualToString:@"SEARCH_EXPERT"]) { + NSDictionary *dict = [response isKindOfClass:[NSDictionary class]] ? response : nil; + NSDictionary *result = dict[@"result"] ?: dict; + NSArray *records = result[@"records"]; + if ([records isKindOfClass:[NSArray class]]) { + [self.dataArray addObjectsFromArray:records]; + } + [self.tableView reloadData]; + } + if ([tag isEqualToString:@"ADDGROUPUSER"]) { + [EasyTextView showSuccessText:@"邀请成功"]; + [self.navigationController popViewControllerAnimated:YES]; + } +} + +@end diff --git a/XinjiangProject/XinjiangProject/Project/SOS/Controller/XJSosCenterViewController.m b/XinjiangProject/XinjiangProject/Project/SOS/Controller/XJSosCenterViewController.m index 287bb1b..1a6e6cc 100644 --- a/XinjiangProject/XinjiangProject/Project/SOS/Controller/XJSosCenterViewController.m +++ b/XinjiangProject/XinjiangProject/Project/SOS/Controller/XJSosCenterViewController.m @@ -286,7 +286,8 @@ [self addWhiteToBlackTransparentGradientToView:self.grapView]; - XMButton * callBtn = [[XMButton alloc] initWithFrame:CGRectMake(kScreenWidth/2 - 70 - kRealValue(33), grapView.height - 17 - 70 , 70, 70)]; + // 120 按钮 + XMButton * callBtn = [[XMButton alloc] initWithFrame:CGRectMake(kScreenWidth/4 - 35, grapView.height - 17 - 70, 70, 70)]; callBtn.style = SDButtonStyleTop; callBtn.backgroundColor = KWhiteColor; callBtn.layer.cornerRadius = 35.0f; @@ -302,8 +303,9 @@ callBtn.imageX = 24; callBtn.interval = 5; [grapView addSubview:callBtn]; - - XMButton * sosBtn = [[XMButton alloc] initWithFrame:CGRectMake(kScreenWidth/2 + kRealValue(33), callBtn.top , 70,70)]; + + // 应急就医 按钮 + XMButton * sosBtn = [[XMButton alloc] initWithFrame:CGRectMake(kScreenWidth/2 - 35, callBtn.top, 70, 70)]; sosBtn.style = SDButtonStyleTop; sosBtn.backgroundColor = KWhiteColor; sosBtn.layer.cornerRadius = 35.0f; @@ -320,6 +322,24 @@ sosBtn.interval = 5; [grapView addSubview:sosBtn]; + // 急救员 按钮 + XMButton * firstAiderBtn = [[XMButton alloc] initWithFrame:CGRectMake(kScreenWidth*3/4 - 35, callBtn.top, 70, 70)]; + firstAiderBtn.style = SDButtonStyleTop; + firstAiderBtn.backgroundColor = KWhiteColor; + firstAiderBtn.layer.cornerRadius = 35.0f; + firstAiderBtn.layer.masksToBounds = true; + [firstAiderBtn setTitle:@"急救员" forState:UIControlStateNormal]; + firstAiderBtn.titleLabel.font = SYSTEMFONT(11); + [firstAiderBtn setTitleColor:UIColorHex(#FF422F) forState:UIControlStateNormal]; + [firstAiderBtn setImage:[UIImage imageNamed:@"sosPeople"] forState:UIControlStateNormal]; + [firstAiderBtn addTarget:self action:@selector(callFirstAiderAction) forControlEvents:UIControlEventTouchUpInside]; + firstAiderBtn.imageW = 22; + firstAiderBtn.imageH = 21; + firstAiderBtn.imageY = 19; + firstAiderBtn.imageX = 24; + firstAiderBtn.interval = 5; + [grapView addSubview:firstAiderBtn]; + self.titles = @[@"油田医院",@"健康小屋",@"AED",@"救护车"]; self.titleView.segmentTitles = self.titles; [self.titleView reloadData]; @@ -502,6 +522,22 @@ }]; } +#pragma mark: 急救员点击 +- (void)callFirstAiderAction { + [self showMBProgressHUDWithString:@"急救发起中,请稍候..." autoHidden:false]; + NSUserDefaults * user = [NSUserDefaults standardUserDefaults]; + CGFloat longitude = [[user objectForKey:USERDEFAULLONG] floatValue]; + CGFloat latitude = [[user objectForKey:USERDEFAULLATITUDE] floatValue]; + + NSMutableDictionary * parmDic = [[NSMutableDictionary alloc] init]; + [parmDic setObject:@(longitude) forKey:@"longitude"]; + [parmDic setObject:@(latitude) forKey:@"latitude"]; + [parmDic setObject:@"急救员" forKey:@"name"]; + + XJPNetAPI * sosApi = [[XJPNetAPI alloc] init:self tag:@"FirstAidGroupAPI" NeedToken:@""]; + [sosApi getCreateGroupMagFirstAider:parmDic]; +} + #pragma mark:应急点击 - (void)callSoSAction { @@ -683,12 +719,12 @@ [self hiddenAllMBProgressHUD]; if ([ESCTUIManager TUIUserStatus] == false) { - - + + [self loginImAccount]; return; } - + SOSGroupIMModel * model = [SOSGroupIMModel modelWithDictionary:dic]; ChatViewController * chatVC = [[ChatViewController alloc] init]; @@ -703,7 +739,25 @@ chatVC.navTitle = @"应急中心"; chatVC.questionType = 1; [self.navigationController pushViewController:chatVC animated:YES]; - + + } + if ([tag isEqualToString:@"FirstAidGroupAPI"]) { + NSDictionary * dic = response; + [self hiddenAllMBProgressHUD]; + + if ([ESCTUIManager TUIUserStatus] == false) { + [self loginImAccount]; + return; + } + + SOSGroupIMModel * model = [SOSGroupIMModel modelWithDictionary:dic]; + ChatViewController * chatVC = [[ChatViewController alloc] init]; + chatVC.isGroupID = YES; + chatVC.isFromFirstAid = YES; + chatVC.isVideo = YES; + chatVC.chatID = model.groupId; + chatVC.navTitle = @"应急就医群"; + [self.navigationController pushViewController:chatVC animated:YES]; } if ([tag isEqualToString:@"loginIMAPI"]) { diff --git a/XinjiangProject/XinjiangProject/Project/SOS/Model/XJFirstAidResourceModel.h b/XinjiangProject/XinjiangProject/Project/SOS/Model/XJFirstAidResourceModel.h new file mode 100644 index 0000000..144ba23 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Project/SOS/Model/XJFirstAidResourceModel.h @@ -0,0 +1,31 @@ +// +// XJFirstAidResourceModel.h +// XinjiangProject +// +// 急救宣教资源 - 详情数据模型 +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface XJFirstAidResourceModel : NSObject + +@property (nonatomic, copy) NSString * resourceId; +@property (nonatomic, copy) NSString * title; +@property (nonatomic, copy) NSString * summary; +@property (nonatomic, copy) NSString * content; // 富文本 HTML +@property (nonatomic, copy) NSString * coverImage; // 封面图 URL +@property (nonatomic, copy) NSString * contentType; // IMAGE_TEXT/VIDEO/AUDIO/PDF +@property (nonatomic, copy) NSString * categoryName; // 分类名称 +@property (nonatomic, copy) NSString * tags; // 标签ID(逗号分隔) +@property (nonatomic, strong) NSArray * tagNameList; // 标签名称列表 +@property (nonatomic, copy) NSString * fileUrl; // 附件 JSON 数组字符串 +@property (nonatomic, assign) NSInteger viewCount; +@property (nonatomic, assign) NSInteger favoriteCount; +@property (nonatomic, assign) BOOL isFavorited; +@property (nonatomic, copy) NSString * createTime; + +@end + +NS_ASSUME_NONNULL_END diff --git a/XinjiangProject/XinjiangProject/Project/SOS/Model/XJFirstAidResourceModel.m b/XinjiangProject/XinjiangProject/Project/SOS/Model/XJFirstAidResourceModel.m new file mode 100644 index 0000000..09e28b1 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Project/SOS/Model/XJFirstAidResourceModel.m @@ -0,0 +1,10 @@ +// +// XJFirstAidResourceModel.m +// XinjiangProject +// + +#import "XJFirstAidResourceModel.h" + +@implementation XJFirstAidResourceModel + +@end diff --git a/XinjiangProject/XinjiangProject/Project/TUIChat/ChatViewController.h b/XinjiangProject/XinjiangProject/Project/TUIChat/ChatViewController.h index 875982a..71ab4d9 100644 --- a/XinjiangProject/XinjiangProject/Project/TUIChat/ChatViewController.h +++ b/XinjiangProject/XinjiangProject/Project/TUIChat/ChatViewController.h @@ -63,6 +63,9 @@ NS_ASSUME_NONNULL_BEGIN ////是否评价过 @property (nonatomic, assign) BOOL isEveal; +// 是否从急救员入口进入 +@property (nonatomic, assign) BOOL isFromFirstAid; + @end NS_ASSUME_NONNULL_END diff --git a/XinjiangProject/XinjiangProject/Project/TUIChat/ChatViewController.m b/XinjiangProject/XinjiangProject/Project/TUIChat/ChatViewController.m index e3a9a24..ba7cc43 100644 --- a/XinjiangProject/XinjiangProject/Project/TUIChat/ChatViewController.m +++ b/XinjiangProject/XinjiangProject/Project/TUIChat/ChatViewController.m @@ -18,6 +18,9 @@ #import "XJExpertMainViewController.h" #import "SendHealthDataViewController.h" #import "CurrentLocationViewController.h" +#import "CustomMapMessageCell.h" +#import "TUIGroupMemberCellData.h" +#import "XJSelectContactViewController.h" #import "TUITextMessageCellData.h" #import "QuestionViewController.h" #import "SendReportModel.h" @@ -29,6 +32,10 @@ #import "PopupView.h" #import "CustomTUIBaseMessageController.h" #import "DoctorHomePageDetailVC.h" +#import "ArticleCardMessageCell.h" +#import "XJFirstAidResourceDetailVC.h" +#import "XJWeChatManager.h" +#import //自定义map @@ -43,7 +50,7 @@ @class TUIChatDataProvider; -@interface ChatViewController () +@interface ChatViewController () @property (nonatomic, strong) TUIC2CChatViewController *vc; @property (nonatomic, strong) TUIGroupChatViewController * groupvc; @@ -61,6 +68,13 @@ @property (nonatomic, copy) NSString * doctorCardID; +// 右侧群成员面板 +@property (nonatomic, strong) UIView *memberPanel; +@property (nonatomic, strong) UIView *memberOverlay; +@property (nonatomic, strong) UITableView *memberTableView; +@property (nonatomic, strong) UILabel *memberTitleLabel; +@property (nonatomic, strong) NSMutableArray *memberList; + @end @implementation ChatViewController @@ -94,6 +108,16 @@ [self addNavigationItemWithImageNames:@[@"whiteBack_icon"] isLeft:YES target:self action:@selector(backUp) tags:@[@200]]; + // 右上角三个点(仅急救员入口显示) + if (self.isFromFirstAid) { + UIButton *moreBtn = [UIButton buttonWithType:UIButtonTypeSystem]; + [moreBtn setTitle:@"···" forState:UIControlStateNormal]; + moreBtn.titleLabel.font = [UIFont boldSystemFontOfSize:22]; + [moreBtn setTitleColor:KWhiteColor forState:UIControlStateNormal]; + [moreBtn addTarget:self action:@selector(moreAction) forControlEvents:UIControlEventTouchUpInside]; + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:moreBtn]; + } + TUIChatConversationModel *data = [[TUIChatConversationModel alloc] init]; HQUserInfo * user = [HQCommonUtils keyedUnarchiverWithKey:LoginUserInfo]; @@ -162,11 +186,12 @@ data.userID = self.chatID; TUIC2CChatViewController *vc = [[TUIC2CChatViewController alloc] init]; self.vc = vc; - vc.messageController.delegate = self; [vc setConversationData:data]; [self addChildViewController:vc]; self.view.backgroundColor = UIColorHex(#EEEEEE); [self.view addSubview:vc.view]; + // addSubview 触发 vc.viewDidLoad 后 messageController 才被创建,必须在这之后设 delegate + vc.messageController.delegate = self; vc.needScrollToBottom = true; vc.customDelegate = self; } @@ -175,14 +200,15 @@ data.groupID = self.chatID; TUIGroupChatViewController * vc = [[TUIGroupChatViewController alloc] init]; self.groupvc = vc; - vc.messageController.delegate = self; [vc setConversationData:data]; [self addChildViewController:vc]; self.view.backgroundColor = UIColorHex(#EEEEEE); [self.view addSubview:vc.view]; + // addSubview 触发 vc.viewDidLoad 后 messageController 才被创建,必须在这之后设 delegate + vc.messageController.delegate = self; vc.needScrollToBottom = true; vc.customDelegate = self; - + } // self.view.backgroundColor = UIColorHex(#EEEEEE); if ([self.tfNew integerValue] == 1) { @@ -277,10 +303,262 @@ -(void)backUp { - + [self.navigationController popViewControllerAnimated:YES]; } +#pragma mark: 右上角更多(群成员侧滑面板) +- (void)moreAction { + if (!self.isGroupID) return; + if (self.memberPanel) { [self hideMemberPanel]; return; } + + UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow; + CGFloat panelW = kScreenWidth - 94; + + // 半透明遮罩 + self.memberOverlay = [[UIView alloc] initWithFrame:keyWindow.bounds]; + self.memberOverlay.backgroundColor = RGBA(0, 0, 0, 0.4); + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideMemberPanel)]; + [self.memberOverlay addGestureRecognizer:tap]; + [keyWindow addSubview:self.memberOverlay]; + + // 右侧面板 + self.memberPanel = [[UIView alloc] initWithFrame:CGRectMake(kScreenWidth, 0, panelW, kScreenHeight)]; + self.memberPanel.backgroundColor = KWhiteColor; + [keyWindow addSubview:self.memberPanel]; + + // 头部区域 + UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, panelW, kStatusBarHeight + 44)]; + headerView.backgroundColor = KWhiteColor; + [self.memberPanel addSubview:headerView]; + + self.memberTitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(12, kStatusBarHeight, panelW - 56, 44)]; + self.memberTitleLabel.text = @"群成员"; + self.memberTitleLabel.textAlignment = NSTextAlignmentLeft; + self.memberTitleLabel.font = [UIFont systemFontOfSize:15 weight:UIFontWeightMedium]; + self.memberTitleLabel.textColor = UIColorHex(0x070809); + [headerView addSubview:self.memberTitleLabel]; + + // 关闭按钮 + UIButton *closeBtn = [[UIButton alloc] initWithFrame:CGRectMake(panelW - 44, kStatusBarHeight, 44, 44)]; + [closeBtn setTitle:@"✕" forState:UIControlStateNormal]; + [closeBtn setTitleColor:UIColorHex(0x070809) forState:UIControlStateNormal]; + [closeBtn addTarget:self action:@selector(hideMemberPanel) forControlEvents:UIControlEventTouchUpInside]; + [headerView addSubview:closeBtn]; + + // 头部分割线 + UIView *headerLine = [[UIView alloc] initWithFrame:CGRectMake(12, headerView.bounds.size.height - 1, panelW - 12, 1)]; + headerLine.backgroundColor = UIColorHex(0xEAECF1); + [headerView addSubview:headerLine]; + + // 成员列表 + CGFloat tableY = kStatusBarHeight + 44; + self.memberTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, tableY, panelW, kScreenHeight - tableY) style:UITableViewStylePlain]; + self.memberTableView.delegate = self; + self.memberTableView.dataSource = self; + self.memberTableView.separatorStyle = UITableViewCellSeparatorStyleNone; + self.memberTableView.rowHeight = 68; + self.memberTableView.tableFooterView = [self makeAddMemberFooterView:panelW]; + [self.memberPanel addSubview:self.memberTableView]; + + // 动画滑入 + [UIView animateWithDuration:0.25 animations:^{ + self.memberPanel.frame = CGRectMake(kScreenWidth - panelW, 0, panelW, kScreenHeight); + }]; + + [self loadGroupMembers]; +} + +- (UIView *)makeAddMemberFooterView:(CGFloat)panelW { + UIView *footer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, panelW, 20 + 49)]; + footer.backgroundColor = KWhiteColor; + + UIButton *addBtn = [[UIButton alloc] initWithFrame:CGRectMake(11, 20, panelW - 22, 49)]; + addBtn.backgroundColor = [UIColorHex(0x2EB8B2) colorWithAlphaComponent:0.12]; + addBtn.layer.cornerRadius = 8; + addBtn.layer.borderWidth = 1; + addBtn.layer.borderColor = UIColorHex(0x2EB8B2).CGColor; + [addBtn addTarget:self action:@selector(addMemberAction) forControlEvents:UIControlEventTouchUpInside]; + + UIImageView *icon = [[UIImageView alloc] initWithFrame:CGRectMake(0, (49 - 18) / 2, 18, 18)]; + icon.image = [UIImage imageNamed:@"firstAid_add_icon"]; + icon.contentMode = UIViewContentModeScaleAspectFit; + [addBtn addSubview:icon]; + + UILabel *btnTitle = [[UILabel alloc] init]; + btnTitle.text = @"添加成员"; + btnTitle.textColor = UIColorHex(0x2EB8B2); + btnTitle.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium]; + [btnTitle sizeToFit]; + CGFloat totalW = 18 + 6 + btnTitle.bounds.size.width; + icon.frame = CGRectMake((addBtn.bounds.size.width - totalW) / 2, (49 - 18) / 2, 18, 18); + btnTitle.frame = CGRectMake(icon.frame.origin.x + 18 + 6, (49 - btnTitle.bounds.size.height) / 2, btnTitle.bounds.size.width, btnTitle.bounds.size.height); + [addBtn addSubview:btnTitle]; + + [footer addSubview:addBtn]; + return footer; +} + +- (void)hideMemberPanel { + CGFloat panelW = kScreenWidth - 94; + [UIView animateWithDuration:0.25 animations:^{ + self.memberPanel.frame = CGRectMake(kScreenWidth, 0, panelW, kScreenHeight); + } completion:^(BOOL finished) { + [self.memberOverlay removeFromSuperview]; + [self.memberPanel removeFromSuperview]; + self.memberOverlay = nil; + self.memberPanel = nil; + self.memberTableView = nil; + self.memberList = nil; + }]; +} + +- (void)loadGroupMembers { + self.memberList = [NSMutableArray array]; + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + [params setObject:self.chatID forKey:@"sessionId"]; + XJPNetAPI *api = [[XJPNetAPI alloc] init:self tag:@"GROUPMEMBERLIST" NeedToken:@""]; + [api getActualGroupMemberList:params]; +} + +/// memberType 转中文 +- (NSString *)memberTypeNameFromValue:(NSInteger)memberType { + switch (memberType) { + case -1: return @"发起人"; + case 0: return @"操作人员"; + case 1: return @"专业人员"; + case 2: return @"员工"; + default: return @""; + } +} + +- (void)addMemberAction { + [self hideMemberPanel]; + XJSelectContactViewController *selectVC = [[XJSelectContactViewController alloc] init]; + selectVC.chatID = self.chatID; + selectVC.groupId = self.chatID; + [self.navigationController pushViewController:selectVC animated:YES]; +} + +#pragma mark - 群成员列表 + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + if (tableView != self.memberTableView || !self.memberList) return 0; + return self.memberList.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + if (tableView != self.memberTableView || indexPath.row >= self.memberList.count) return [UITableViewCell new]; + static NSString *cellId = @"MemberCell"; + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId]; + if (!cell) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId]; + cell.selectionStyle = UITableViewCellSelectionStyleNone; + + CGFloat panelW = kScreenWidth - 94; + + // 头像 + UIImageView *avatar = [[UIImageView alloc] initWithFrame:CGRectMake(12, 11, 45, 45)]; + avatar.tag = 100; + avatar.layer.cornerRadius = 8; + avatar.layer.masksToBounds = YES; + [cell.contentView addSubview:avatar]; + + // 姓名 + UILabel *nameLabel = [[UILabel alloc] init]; + nameLabel.tag = 101; + nameLabel.font = [UIFont systemFontOfSize:15 weight:UIFontWeightMedium]; + nameLabel.textColor = UIColorHex(0x070809); + [cell.contentView addSubview:nameLabel]; + + // 本人标识 + UILabel *selfBadge = [[UILabel alloc] init]; + selfBadge.tag = 105; + selfBadge.text = @"本人"; + selfBadge.font = [UIFont systemFontOfSize:10 weight:UIFontWeightSemibold]; + selfBadge.textColor = UIColorHex(0x2EB8B2); + selfBadge.backgroundColor = [UIColorHex(0x2EB8B2) colorWithAlphaComponent:0.12]; + selfBadge.textAlignment = NSTextAlignmentCenter; + selfBadge.layer.cornerRadius = 4; + selfBadge.layer.masksToBounds = YES; + selfBadge.hidden = YES; + [cell.contentView addSubview:selfBadge]; + + // 成员类型 + UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(70, 42, panelW - 132, 14)]; + titleLabel.tag = 102; + titleLabel.font = [UIFont systemFontOfSize:12 weight:UIFontWeightRegular]; + titleLabel.textColor = UIColorHex(0x808080); + [cell.contentView addSubview:titleLabel]; + + // 电话按钮 + UIButton *phoneBtn = [[UIButton alloc] initWithFrame:CGRectMake(panelW - 47, 20, 32, 32)]; + phoneBtn.tag = 103; + [phoneBtn setImage:[UIImage imageNamed:@"firstAid_phone_icon"] forState:UIControlStateNormal]; + [phoneBtn addTarget:self action:@selector(callMemberAction:) forControlEvents:UIControlEventTouchUpInside]; + [cell.contentView addSubview:phoneBtn]; + + // 分割线 + UIView *line = [[UIView alloc] initWithFrame:CGRectMake(70, 67, panelW - 70, 1)]; + line.tag = 104; + line.backgroundColor = UIColorHex(0xEAECF1); + [cell.contentView addSubview:line]; + } + + NSDictionary *member = self.memberList[indexPath.row]; + UIImageView *avatar = [cell.contentView viewWithTag:100]; + UILabel *nameLabel = [cell.contentView viewWithTag:101]; + UILabel *titleLabel = [cell.contentView viewWithTag:102]; + UILabel *selfBadge = [cell.contentView viewWithTag:105]; + + CGFloat panelW = kScreenWidth - 94; + + // 姓名 + NSString *userName = member[@"userName"] ?: @""; + nameLabel.text = userName; + CGSize nameSize = [userName sizeWithAttributes:@{NSFontAttributeName: nameLabel.font}]; + nameLabel.frame = CGRectMake(70, 15, nameSize.width, 17); + + // 本人标识 + BOOL isSelfUser = [member[@"isSelf"] integerValue] == 1; + if (isSelfUser) { + selfBadge.hidden = NO; + selfBadge.frame = CGRectMake(CGRectGetMaxX(nameLabel.frame) + 6, 16, 30, 16); + } else { + selfBadge.hidden = YES; + } + + // 成员类型 + titleLabel.text = [self memberTypeNameFromValue:[member[@"memberType"] integerValue]]; + + NSString *avatarPath = member[@"avatar"]; + if (avatarPath.length > 0) { + NSString *fullUrl = [avatarPath hasPrefix:@"http"] ? avatarPath : [NSString stringWithFormat:@"%@%@", ResourceAddress, avatarPath]; + [avatar sd_setImageWithURL:[NSURL URLWithString:fullUrl] placeholderImage:[UIImage imageNamed:@"heard_man"]]; + } else { + avatar.image = [UIImage imageNamed:@"heard_man"]; + } + + return cell; +} + +- (void)callMemberAction:(UIButton *)sender { + UITableViewCell *cell = (UITableViewCell *)sender.superview.superview; + NSIndexPath *indexPath = [self.memberTableView indexPathForCell:cell]; + if (!indexPath || indexPath.row >= self.memberList.count) return; + NSDictionary *member = self.memberList[indexPath.row]; + NSString *phone = member[@"phone"]; + if (phone.length == 0) { + [EasyTextView showErrorText:@"暂无手机号"]; + return; + } + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"tel://%@", phone]] options:nil completionHandler:nil]; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + [tableView deselectRowAtIndexPath:indexPath animated:YES]; +} + #pragma mark:跳转到地图选择页 - (void)jumpChatMapView { @@ -494,6 +772,15 @@ experVC.danganID = healthCell.fileId; [self.navigationController pushViewController:experVC animated:YES]; } + + if ([cell isKindOfClass:ArticleCardMessageCell.class]) { + ArticleCardMessageCell * articleCell = (ArticleCardMessageCell *)cell; + if (articleCell.resourceId.length > 0) { + XJFirstAidResourceDetailVC * detailVC = [[XJFirstAidResourceDetailVC alloc] init]; + detailVC.resourceId = articleCell.resourceId; + [self.navigationController pushViewController:detailVC animated:YES]; + } + } } - (void)getDocotorInfo:(long)sendTime{ @@ -599,17 +886,26 @@ } if ([tag isEqualToString:@"smallAPI"]) { - + bool isSmallHelp = [response boolValue]; - - + + if (isSmallHelp == true) { - + DoctorHomePageDetailVC * vc = [[DoctorHomePageDetailVC alloc] init]; vc.userID = self.userID; [self.navigationController pushViewController:vc animated:YES]; } } + if ([tag isEqualToString:@"GROUPMEMBERLIST"]) { + [self.memberList removeAllObjects]; + NSArray *list = [response isKindOfClass:[NSDictionary class]] ? response[@"result"] : response; + if ([list isKindOfClass:[NSArray class]]) { + [self.memberList addObjectsFromArray:list]; + } + self.memberTitleLabel.text = [NSString stringWithFormat:@"群成员(%lu)", (unsigned long)self.memberList.count]; + [self.memberTableView reloadData]; + } } @@ -938,8 +1234,8 @@ // 处理授权成功的情况(例如,开始更新位置信息) ChatMapViewController * mapVC = [[ChatMapViewController alloc] init]; MJWeakSelf - mapVC.mapBlcok = ^(CLLocationCoordinate2D coordinate) { - V2TIMMessage * message = [[V2TIMManager sharedInstance] createLocationMessage:@"位置" longitude:coordinate.longitude latitude:coordinate.latitude]; + mapVC.mapBlcok = ^(CLLocationCoordinate2D coordinate, NSString *address) { + V2TIMMessage * message = [[V2TIMManager sharedInstance] createLocationMessage:address ?: @"位置" longitude:coordinate.longitude latitude:coordinate.latitude]; if( self.isGroupID == false) { [weakSelf.vc sendMessage:message]; }else @@ -983,4 +1279,93 @@ //} +#pragma mark - ————— 地图卡片长按转发微信 ————— + +- (void)messageController:(TUIBaseMessageController *)controller willDisplayCell:(TUIMessageCell *)cell withData:(TUIMessageCellData *)data { + // 运行时判断,避免直接 import Pods 中未被暴露的头文件 + Class mapCellClass = NSClassFromString(@"MapMessageCustomCell"); + BOOL isMapCell = [cell isKindOfClass:mapCellClass]; + NSLog(@"[ChatVC] willDisplayCell: cell=%@, isMapCell=%d, class=%@", cell, isMapCell, NSStringFromClass([cell class])); + if (isMapCell) { + if ([cell respondsToSelector:@selector(setMapDelegate:)]) { + [cell performSelector:@selector(setMapDelegate:) withObject:self]; + NSLog(@"[ChatVC] mapDelegate set to self on %@", cell); + } + } +} + +- (void)mapCellDidLongPressWithData:(id)data { + NSLog(@"[ChatVC] mapCellDidLongPress called, data=%@", data); + if (![[XJWeChatManager sharedManager] isWeChatInstalled]) { + [EasyTextView showErrorText:@"未安装微信"]; + return; + } + + UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; + [alert addAction:[UIAlertAction actionWithTitle:@"转发到微信" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + [self shareMapToWeChatWithData:data]; + }]]; + [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]]; + + UIViewController *topVC = [self topViewController]; + [topVC presentViewController:alert animated:YES completion:nil]; +} + +- (void)shareMapToWeChatWithData:(id)data { + NSString *address = [data valueForKey:@"text"]; + if (![address isKindOfClass:[NSString class]] || address.length == 0) { + address = @"我的位置"; + } + CGFloat lat = [[data valueForKey:@"latitude"] doubleValue]; + CGFloat lng = [[data valueForKey:@"longitude"] doubleValue]; + + NSString *encodedAddress = [address stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; + NSString *mapUrl = [NSString stringWithFormat:@"https://uri.amap.com/marker?position=%f,%f&name=%@", + lng, lat, encodedAddress ?: @"位置"]; + + NSString *imageKey = [data valueForKey:@"MapPictureImg"]; + UIImage *thumb = [[SDImageCache sharedImageCache] imageFromCacheForKey:imageKey]; + + [[XJWeChatManager sharedManager] shareWebpageWithTitle:@"我的位置" + desc:address + webpageUrl:mapUrl + thumbImage:thumb + toScene:0 + completion:^(BOOL success, NSString * _Nullable errorMsg) { + if (!success && errorMsg.length > 0) { + [EasyTextView showErrorText:errorMsg]; + } + }]; +} + +- (UIViewController *)topViewController { + UIViewController *rootVC = [UIApplication sharedApplication].keyWindow.rootViewController; + while (rootVC.presentedViewController) { + rootVC = rootVC.presentedViewController; + } + if ([rootVC isKindOfClass:[UINavigationController class]]) { + return [(UINavigationController *)rootVC topViewController]; + } + if ([rootVC isKindOfClass:[UITabBarController class]]) { + UIViewController *selected = [(UITabBarController *)rootVC selectedViewController]; + if ([selected isKindOfClass:[UINavigationController class]]) { + return [(UINavigationController *)selected topViewController]; + } + return selected; + } + return rootVC; +} + +/// 将未实现的 TUIBaseMessageControllerDelegate 方法转发给 TUIBaseChatViewController(self.vc / self.groupvc) +- (id)forwardingTargetForSelector:(SEL)aSelector { + // 优先转发给 C2C 的 vc,其次 groupvc + if ([self.vc respondsToSelector:aSelector]) { + return self.vc; + } + if ([self.groupvc respondsToSelector:aSelector]) { + return self.groupvc; + } + return [super forwardingTargetForSelector:aSelector]; +} + @end diff --git a/XinjiangProject/XinjiangProject/Project/TUIChat/Controller/ChatMapViewController.h b/XinjiangProject/XinjiangProject/Project/TUIChat/Controller/ChatMapViewController.h index 2791b39..b7a02ef 100644 --- a/XinjiangProject/XinjiangProject/Project/TUIChat/Controller/ChatMapViewController.h +++ b/XinjiangProject/XinjiangProject/Project/TUIChat/Controller/ChatMapViewController.h @@ -9,7 +9,7 @@ NS_ASSUME_NONNULL_BEGIN -typedef void(^sendUserSoSLocation)(CLLocationCoordinate2D coordinate); +typedef void(^sendUserSoSLocation)(CLLocationCoordinate2D coordinate, NSString *address); @interface ChatMapViewController : XJBaseViewController diff --git a/XinjiangProject/XinjiangProject/Project/TUIChat/Controller/ChatMapViewController.m b/XinjiangProject/XinjiangProject/Project/TUIChat/Controller/ChatMapViewController.m index dc74b83..dd5badb 100644 --- a/XinjiangProject/XinjiangProject/Project/TUIChat/Controller/ChatMapViewController.m +++ b/XinjiangProject/XinjiangProject/Project/TUIChat/Controller/ChatMapViewController.m @@ -14,6 +14,8 @@ @property (nonatomic, strong) MAMapView *mapView; @property (nonatomic, strong) AMapSearchAPI *search; @property (nonatomic, assign) BOOL userFirstLoca; +@property (nonatomic, copy) NSString *selectedAddress; +@property (nonatomic, strong) UILabel *addressLabel; @end @implementation ChatMapViewController @@ -50,22 +52,46 @@ } - (void)createUI { - + self.mapView = [[MAMapView alloc] initWithFrame:self.view.bounds]; self.mapView.zoomLevel = 17; self.mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; self.mapView.userTrackingMode = MAUserTrackingModeFollow; [self.view addSubview:self.mapView]; + self.search = [[AMapSearchAPI alloc] init]; self.search.delegate = self; self.mapView.delegate = self; [self addNavigationItemWithTitles:@[@"发送"] isLeft:NO target:self action:@selector(sendMapmessate) tags:@[@"100"]]; - + + // 地址标签容器(大头针上方,灰色半透明圆角) + UIView *addressBgView = [[UIView alloc] init]; + addressBgView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.55]; + addressBgView.layer.cornerRadius = 8; + addressBgView.layer.masksToBounds = YES; + [self.view addSubview:addressBgView]; + [self.view bringSubviewToFront:addressBgView]; + [addressBgView mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerX.equalTo(self.view); + make.bottom.equalTo(self.view.mas_centerY).offset(-35); + make.width.lessThanOrEqualTo(@(kScreenWidth - 60)); + }]; + + self.addressLabel = [[UILabel alloc] init]; + self.addressLabel.text = @"定位中..."; + self.addressLabel.numberOfLines = 2; + self.addressLabel.textAlignment = NSTextAlignmentCenter; + self.addressLabel.font = SYSTEMFONT(14); + self.addressLabel.textColor = KWhiteColor; + [addressBgView addSubview:self.addressLabel]; + [self.addressLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.equalTo(addressBgView).insets(UIEdgeInsetsMake(8, 12, 8, 12)); + }]; } - (void)sendMapmessate { - + if (self.mapBlcok) { - self.mapBlcok(coordinate); + self.mapBlcok(coordinate, self.selectedAddress); } [self.navigationController popViewControllerAnimated:YES]; @@ -101,7 +127,6 @@ // 目的是只用第一次定位的数据,即用户的初始位置 if (self.userFirstLoca == NO) { self.userFirstLoca = YES; -// self.userFirstCoor = userLocation.location.coordinate; coordinate = userLocation.location.coordinate; MAPointAnnotation *ann = [[MAPointAnnotation alloc] init]; ann.coordinate = userLocation.location.coordinate; @@ -109,6 +134,12 @@ ann.lockedToScreen = YES; ann.lockedScreenPoint = CGPointMake(mapView.center.x, mapView.center.y); [mapView addAnnotation:ann]; + + // 首次定位后立即反查地址 + AMapReGeocodeSearchRequest *rego = [[AMapReGeocodeSearchRequest alloc] init]; + rego.requireExtension = YES; + rego.location = [AMapGeoPoint locationWithLatitude:coordinate.latitude longitude:coordinate.longitude]; + [self.search AMapReGoecodeSearch:rego]; } } @@ -116,11 +147,11 @@ - (void)mapView:(MAMapView *)mapView mapDidMoveByUser:(BOOL)wasUserAction { // 将指定view坐标系的坐标转换为经纬度 - CLLocationCoordinate2D moveCoor = [mapView convertPoint:CGPointMake(mapView.center.x, mapView.center.y-64) toCoordinateFromView:mapView]; - + CLLocationCoordinate2D moveCoor = [mapView convertPoint:CGPointMake(mapView.center.x, mapView.center.y) toCoordinateFromView:mapView]; + coordinate = moveCoor; + // 通过坐标点得到经纬度,从而进行反地理编码 AMapReGeocodeSearchRequest *rego = [[AMapReGeocodeSearchRequest alloc] init]; - // 是否返回扩展信(因为我们需要得到附近地理位置信息,所以设为YES) rego.requireExtension = YES; rego.location = [AMapGeoPoint locationWithLatitude:moveCoor.latitude longitude:moveCoor.longitude]; [self.search AMapReGoecodeSearch:rego]; @@ -129,7 +160,8 @@ /* 逆地理编码查询回调函数 */ - (void)onReGeocodeSearchDone:(AMapReGeocodeSearchRequest *)request response:(AMapReGeocodeSearchResponse *)response { - // reGeodeVC就是一个简单的tableView布局,数据来源就是response.regeocode.pois + self.selectedAddress = response.regeocode.formattedAddress; + self.addressLabel.text = self.selectedAddress; } /* 这个是reGeodeVC点击tableViewCell的回调 */ diff --git a/XinjiangProject/XinjiangProject/Project/TUIChat/CustomChatCell/ArticleCardCell/ArticleCardCellData.h b/XinjiangProject/XinjiangProject/Project/TUIChat/CustomChatCell/ArticleCardCell/ArticleCardCellData.h new file mode 100644 index 0000000..0102980 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Project/TUIChat/CustomChatCell/ArticleCardCell/ArticleCardCellData.h @@ -0,0 +1,21 @@ +// +// ArticleCardCellData.h +// XinjiangProject +// +// 急救宣教推送 - 自定义消息数据模型 +// + +#import "TUIMessageCellData.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface ArticleCardCellData : TUIMessageCellData + +@property (nonatomic, copy) NSString *imageUrl; // 封面图 URL(可为空) +@property (nonatomic, copy) NSString *title; // 标题 +@property (nonatomic, copy) NSString *content; // 内容摘要 +@property (nonatomic, copy) NSString *resourceId; // 宣教资源ID + +@end + +NS_ASSUME_NONNULL_END diff --git a/XinjiangProject/XinjiangProject/Project/TUIChat/CustomChatCell/ArticleCardCell/ArticleCardCellData.m b/XinjiangProject/XinjiangProject/Project/TUIChat/CustomChatCell/ArticleCardCell/ArticleCardCellData.m new file mode 100644 index 0000000..9b95fe3 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Project/TUIChat/CustomChatCell/ArticleCardCell/ArticleCardCellData.m @@ -0,0 +1,63 @@ +// +// ArticleCardCellData.m +// XinjiangProject +// + +#import "ArticleCardCellData.h" + +@implementation ArticleCardCellData + ++ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message { + NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data + options:NSJSONReadingAllowFragments + error:nil]; + ArticleCardCellData *cellData = [[ArticleCardCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming]; + cellData.innerMessage = message; + cellData.msgID = message.msgID; + cellData.avatarUrl = [NSURL URLWithString:message.faceURL]; + cellData.reuseId = @"ArticleCardMessageCell"; + + // 过滤 nil 值,避免 OC 把 nil 转成 "(null)" 字符串 + id imageUrlObj = param[@"messageImageUrl"]; + id titleObj = param[@"title"]; + id contentObj = param[@"content"]; + + id resourceIdObj = param[@"resourceId"]; + + cellData.imageUrl = ([imageUrlObj isKindOfClass:[NSString class]] && [imageUrlObj length] > 0) ? imageUrlObj : nil; + cellData.title = ([titleObj isKindOfClass:[NSString class]]) ? titleObj : @""; + cellData.content = ([contentObj isKindOfClass:[NSString class]]) ? contentObj : @""; + cellData.resourceId = ([resourceIdObj isKindOfClass:[NSString class]]) ? resourceIdObj : @""; + + return cellData; +} + +- (CGSize)contentSize { + CGFloat cardWidth = 260.f; + + CGFloat textAreaWidth; + if (self.imageUrl.length > 0) { + textAreaWidth = cardWidth - 16 - 50 - 12 - 16; // 左边距 + 图片宽 + 间距 + 右边距 + } else { + textAreaWidth = cardWidth - 16 - 16; // 左右边距各16 + } + + // 标题单行高度 + CGFloat titleHeight = 22.f; + + // 内容最多2行 + CGFloat contentMaxHeight = 40.f; + CGRect contentRect = [self.content boundingRectWithSize:CGSizeMake(textAreaWidth, contentMaxHeight) + options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading + attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:13]} + context:nil]; + CGFloat contentHeight = MIN(ceilf(contentRect.size.height), contentMaxHeight); + + CGFloat textTotalHeight = titleHeight + 6 + contentHeight; + CGFloat imageHeight = (self.imageUrl.length > 0) ? 50.f : 0.f; + CGFloat totalHeight = 14 + MAX(textTotalHeight, imageHeight) + 14; + + return CGSizeMake(cardWidth, totalHeight); +} + +@end diff --git a/XinjiangProject/XinjiangProject/Project/TUIChat/CustomChatCell/ArticleCardCell/ArticleCardMessageCell.h b/XinjiangProject/XinjiangProject/Project/TUIChat/CustomChatCell/ArticleCardCell/ArticleCardMessageCell.h new file mode 100644 index 0000000..3a0a5a7 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Project/TUIChat/CustomChatCell/ArticleCardCell/ArticleCardMessageCell.h @@ -0,0 +1,32 @@ +// +// ArticleCardMessageCell.h +// XinjiangProject +// +// 急救宣教推送 - 自定义消息气泡 +// ┌─────────────────────────────────┐ +// │ 白色圆角卡片 │ +// │ ┌────────┬────────────────────┐ │ +// │ │ 封面图 │ 标题(15pt #1A1A1A)│ │ +// │ │ 80×60 │ 内容(13pt #888) │ │ +// │ └────────┴────────────────────┘ │ +// │ (无封面时标题和内容直接左边距) │ +// └─────────────────────────────────┘ +// + +#import "TUIMessageCell.h" +#import "ArticleCardCellData.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface ArticleCardMessageCell : TUIMessageCell + +@property (nonatomic, strong) UIImageView *coverImageView; // 封面图 +@property (nonatomic, strong) UILabel *titleLabel; // 标题 +@property (nonatomic, strong) UILabel *contentLabel; // 内容摘要 +@property (nonatomic, copy) NSString *resourceId; // 宣教资源ID + +- (void)fillWithData:(ArticleCardCellData *)data; + +@end + +NS_ASSUME_NONNULL_END diff --git a/XinjiangProject/XinjiangProject/Project/TUIChat/CustomChatCell/ArticleCardCell/ArticleCardMessageCell.m b/XinjiangProject/XinjiangProject/Project/TUIChat/CustomChatCell/ArticleCardCell/ArticleCardMessageCell.m new file mode 100644 index 0000000..e26ba4f --- /dev/null +++ b/XinjiangProject/XinjiangProject/Project/TUIChat/CustomChatCell/ArticleCardCell/ArticleCardMessageCell.m @@ -0,0 +1,123 @@ +// +// ArticleCardMessageCell.m +// XinjiangProject +// + +#import "ArticleCardMessageCell.h" + +// 布局常量 +static CGFloat const kCardPaddingH = 16.f; // 卡片水平内边距 +static CGFloat const kCardPaddingV = 14.f; // 卡片垂直内边距 +static CGFloat const kCoverImageWidth = 50.f; // 封面图宽度(对齐医生卡片头像) +static CGFloat const kCoverImageHeight = 50.f; // 封面图高度 +static CGFloat const kCoverImageRadius = 6.f; // 封面图圆角 +static CGFloat const kCoverTextGap = 12.f; // 封面图与文字间距 +static CGFloat const kTitleContentGap = 6.f; // 标题与内容间距 + +@implementation ArticleCardMessageCell + +- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { + if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { + [self createUI]; + } + return self; +} + +- (void)createUI { + self.container.layer.cornerRadius = 10; + self.container.backgroundColor = KWhiteColor; + + // 封面图 + self.coverImageView = [[UIImageView alloc] init]; + self.coverImageView.layer.masksToBounds = YES; + self.coverImageView.layer.cornerRadius = kCoverImageRadius; + self.coverImageView.contentMode = UIViewContentModeScaleAspectFill; + self.coverImageView.hidden = YES; + [self.container addSubview:self.coverImageView]; + + // 标题 + self.titleLabel = [[UILabel alloc] init]; + self.titleLabel.font = [UIFont systemFontOfSize:15 weight:UIFontWeightMedium]; + self.titleLabel.textColor = UIColorHex(0x1A1A1A); + self.titleLabel.textAlignment = NSTextAlignmentLeft; + self.titleLabel.numberOfLines = 1; + [self.container addSubview:self.titleLabel]; + + // 内容摘要 + self.contentLabel = [[UILabel alloc] init]; + self.contentLabel.font = [UIFont systemFontOfSize:13 weight:UIFontWeightRegular]; + self.contentLabel.textColor = UIColorHex(0x888888); + self.contentLabel.textAlignment = NSTextAlignmentLeft; + self.contentLabel.numberOfLines = 2; + [self.container addSubview:self.contentLabel]; +} + +- (void)fillWithData:(ArticleCardCellData *)data { + [super fillWithData:data]; + + self.titleLabel.text = data.title; + self.contentLabel.text = data.content; + self.resourceId = data.resourceId; + + BOOL hasImage = (data.imageUrl.length > 0); + self.coverImageView.hidden = !hasImage; + + if (hasImage) { + NSString *encodedUrl = [self encodeImageURL:data.imageUrl]; + [self.coverImageView sd_setImageWithURL:[NSURL URLWithString:encodedUrl] + placeholderImage:nil]; + } +} + +- (void)layoutSubviews { + [super layoutSubviews]; + + CGFloat containerW = self.container.bounds.size.width; + BOOL hasImage = !self.coverImageView.hidden; + CGFloat textOriginX; + CGFloat textMaxW; + + if (hasImage) { + // 封面图:左侧 + self.coverImageView.frame = CGRectMake(kCardPaddingH, + kCardPaddingV, + kCoverImageWidth, + kCoverImageHeight); + textOriginX = CGRectGetMaxX(self.coverImageView.frame) + kCoverTextGap; + textMaxW = containerW - textOriginX - kCardPaddingH; + } else { + textOriginX = kCardPaddingH; + textMaxW = containerW - kCardPaddingH - kCardPaddingH; + } + + // 标题 + self.titleLabel.frame = CGRectMake(textOriginX, + kCardPaddingV, + textMaxW, + 22); + + // 内容(标题下方) + CGFloat contentY = CGRectGetMaxY(self.titleLabel.frame) + kTitleContentGap; + CGFloat contentHeight = self.container.bounds.size.height - kCardPaddingV - contentY; + self.contentLabel.frame = CGRectMake(textOriginX, + contentY, + textMaxW, + contentHeight); +} + +#pragma mark - URL 编码 + +- (NSString *)encodeImageURL:(NSString *)url { + if (![url containsString:@"https"] && ![url containsString:@"http"]) { + url = [NSString stringWithFormat:@"%@%@", ResourceAddress, url]; + } + NSString *str1 = @"\\"; + url = [url stringByReplacingOccurrencesOfString:str1 withString:@"/"]; + return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, + (CFStringRef)url, + (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", + NULL, + kCFStringEncodingUTF8)); +} + +@end diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_add_icon.imageset/Contents.json b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_add_icon.imageset/Contents.json new file mode 100644 index 0000000..ada4361 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_add_icon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "firstAid_phone_icon.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_add_icon.imageset/firstAid_phone_icon.png b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_add_icon.imageset/firstAid_phone_icon.png new file mode 100644 index 0000000..b3067dc Binary files /dev/null and b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_add_icon.imageset/firstAid_phone_icon.png differ diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_checkbox_selected.imageset/Contents.json b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_checkbox_selected.imageset/Contents.json new file mode 100644 index 0000000..db785aa --- /dev/null +++ b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_checkbox_selected.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "firstAid_checkbox_selected.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_checkbox_selected.imageset/firstAid_checkbox_selected.png b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_checkbox_selected.imageset/firstAid_checkbox_selected.png new file mode 100644 index 0000000..7af46ba Binary files /dev/null and b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_checkbox_selected.imageset/firstAid_checkbox_selected.png differ diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_checkbox_unselected.imageset/Contents.json b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_checkbox_unselected.imageset/Contents.json new file mode 100644 index 0000000..02c7018 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_checkbox_unselected.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "firstAid_checkbox_unselected.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_checkbox_unselected.imageset/firstAid_checkbox_unselected.png b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_checkbox_unselected.imageset/firstAid_checkbox_unselected.png new file mode 100644 index 0000000..b187f7e Binary files /dev/null and b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_checkbox_unselected.imageset/firstAid_checkbox_unselected.png differ diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_phone_icon.imageset/Contents.json b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_phone_icon.imageset/Contents.json new file mode 100644 index 0000000..ada4361 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_phone_icon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "firstAid_phone_icon.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_phone_icon.imageset/firstAid_phone_icon.png b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_phone_icon.imageset/firstAid_phone_icon.png new file mode 100644 index 0000000..979b597 Binary files /dev/null and b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/firstAid_phone_icon.imageset/firstAid_phone_icon.png differ diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_bp.imageset/Contents.json b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_bp.imageset/Contents.json new file mode 100644 index 0000000..aae6a18 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_bp.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "health_icon_bp.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_bp.imageset/health_icon_bp.png b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_bp.imageset/health_icon_bp.png new file mode 100644 index 0000000..47a1767 Binary files /dev/null and b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_bp.imageset/health_icon_bp.png differ diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_glu.imageset/Contents.json b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_glu.imageset/Contents.json new file mode 100644 index 0000000..ede4b95 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_glu.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "health_icon_glu.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_glu.imageset/health_icon_glu.png b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_glu.imageset/health_icon_glu.png new file mode 100644 index 0000000..ae78223 Binary files /dev/null and b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_glu.imageset/health_icon_glu.png differ diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_lipid.imageset/Contents.json b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_lipid.imageset/Contents.json new file mode 100644 index 0000000..a1b342b --- /dev/null +++ b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_lipid.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "health_icon_lipid.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_lipid.imageset/health_icon_lipid.png b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_lipid.imageset/health_icon_lipid.png new file mode 100644 index 0000000..72fcd1a Binary files /dev/null and b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_lipid.imageset/health_icon_lipid.png differ diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_ua.imageset/Contents.json b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_ua.imageset/Contents.json new file mode 100644 index 0000000..2f72ceb --- /dev/null +++ b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_ua.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "health_icon_ua.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_ua.imageset/health_icon_ua.png b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_ua.imageset/health_icon_ua.png new file mode 100644 index 0000000..8e6a86f Binary files /dev/null and b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_icon_ua.imageset/health_icon_ua.png differ diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_medical_deco.imageset/Contents.json b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_medical_deco.imageset/Contents.json new file mode 100644 index 0000000..55348ba --- /dev/null +++ b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_medical_deco.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "health_medical_deco.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_medical_deco.imageset/health_medical_deco.png b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_medical_deco.imageset/health_medical_deco.png new file mode 100644 index 0000000..be2f172 Binary files /dev/null and b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_medical_deco.imageset/health_medical_deco.png differ diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_status_dot.imageset/Contents.json b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_status_dot.imageset/Contents.json new file mode 100644 index 0000000..e60340f --- /dev/null +++ b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_status_dot.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "微信图片_20260707155356_208_97.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_status_dot.imageset/微信图片_20260707155356_208_97.png b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_status_dot.imageset/微信图片_20260707155356_208_97.png new file mode 100644 index 0000000..9a64569 Binary files /dev/null and b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/health_status_dot.imageset/微信图片_20260707155356_208_97.png differ diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/opinion_line_left.imageset/Contents.json b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/opinion_line_left.imageset/Contents.json new file mode 100644 index 0000000..8491d73 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/opinion_line_left.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "opinion_line_right.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/opinion_line_left.imageset/opinion_line_right.png b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/opinion_line_left.imageset/opinion_line_right.png new file mode 100644 index 0000000..5050dbd Binary files /dev/null and b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/opinion_line_left.imageset/opinion_line_right.png differ diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/opinion_line_right.imageset/Contents.json b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/opinion_line_right.imageset/Contents.json new file mode 100644 index 0000000..d234dc8 --- /dev/null +++ b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/opinion_line_right.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "opinion_line_left.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/opinion_line_right.imageset/opinion_line_left.png b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/opinion_line_right.imageset/opinion_line_left.png new file mode 100644 index 0000000..ab9f4c0 Binary files /dev/null and b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/HOMEV2/opinion_line_right.imageset/opinion_line_left.png differ diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/other/navBackImg.imageset/Contents.json b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/other/navBackImg.imageset/Contents.json index 4426bf8..00d80e8 100644 --- a/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/other/navBackImg.imageset/Contents.json +++ b/XinjiangProject/XinjiangProject/Supporting Files/Assets.xcassets/other/navBackImg.imageset/Contents.json @@ -1,11 +1,11 @@ { "images" : [ { - "filename" : "navBackImg.png", "idiom" : "universal", "scale" : "1x" }, { + "filename" : "navBackImg.png", "idiom" : "universal", "scale" : "2x" }, diff --git a/XinjiangProject/XinjiangProject/Supporting Files/Info.plist b/XinjiangProject/XinjiangProject/Supporting Files/Info.plist index 096368c..5c53c7d 100644 --- a/XinjiangProject/XinjiangProject/Supporting Files/Info.plist +++ b/XinjiangProject/XinjiangProject/Supporting Files/Info.plist @@ -18,6 +18,14 @@ CFBundleTypeRole Editor + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + wxb33cd60beda0a212 + + LSApplicationQueriesSchemes @@ -25,6 +33,8 @@ comgooglemaps baidumap iosamap + weixin + weixinULAPI Localization native development region China @@ -40,5 +50,7 @@ UISceneConfigurations + UIDesignRequiresCompatibility + diff --git a/XinjiangProject/XinjiangProject/XinjiangProject.entitlements b/XinjiangProject/XinjiangProject/XinjiangProject.entitlements new file mode 100644 index 0000000..7b17f14 --- /dev/null +++ b/XinjiangProject/XinjiangProject/XinjiangProject.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.developer.associated-domains + + applinks:www.rk-health.com + + +