feat(sos): 集成微信SDK,实现地图卡片长按转发微信;新增急救资源详情页及群成员面板
- 手动集成微信 OpenSDK-NoPay,创建 XJWeChatManager 统一管理分享能力 - 地图卡片新增长按手势,通过代理模式实现「转发到微信」,分享含高德导航链接 - 聊天页新增群成员侧滑面板,支持查看成员列表及一键拨号 - 新增急救资源详情页(XJFirstAidResourceDetailVC)及选择联系人页 - 首页健康知识/资讯模块,含双视图列表及 H5 跳转 - 修复急救资源详情页编译报错 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@@ -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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
@@ -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";
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//
|
||||
|
||||
#import "JKCQNetworkingWithCache.h"
|
||||
|
||||
#import <CommonCrypto/CommonHMAC.h>
|
||||
#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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>AvailableLibraries</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>WechatOpenSDK.framework/WechatOpenSDK</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>ios-arm64</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>WechatOpenSDK.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>ios</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>BinaryPath</key>
|
||||
<string>WechatOpenSDK.framework/WechatOpenSDK</string>
|
||||
<key>LibraryIdentifier</key>
|
||||
<string>ios-arm64_x86_64-simulator</string>
|
||||
<key>LibraryPath</key>
|
||||
<string>WechatOpenSDK.framework</string>
|
||||
<key>SupportedArchitectures</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
<string>x86_64</string>
|
||||
</array>
|
||||
<key>SupportedPlatform</key>
|
||||
<string>ios</string>
|
||||
<key>SupportedPlatformVariant</key>
|
||||
<string>simulator</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XFWK</string>
|
||||
<key>XCFrameworkFormatVersion</key>
|
||||
<string>1.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>CA92.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,167 @@
|
||||
重要!
|
||||
SDK2.0.5
|
||||
1. 支持模块化集成:XCFramework 头文件引用改为标准化格式 #import <WechatOpenSDK/WXApi.h>,解决路径冲突并支持 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”里增加如下代码:
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>weixin</string>
|
||||
</array>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
2、开发者需要在工程中链接上 CoreTelephony.framework
|
||||
3、解决bitcode编译不过问题
|
||||
|
||||
SDK1.5
|
||||
1、废弃safeSendReq:接口,使用sendReq:即可。
|
||||
2、新增+(BOOL) sendAuthReq:(SendAuthReq*) req viewController : (UIViewController*) viewController delegate:(id<WXApiDelegate>) delegate;
|
||||
支持未安装微信情况下Auth,具体见WXApi.h接口描述
|
||||
3、微信开放平台新增了微信模块用户统计功能,便于开发者统计微信功能模块的用户使用和活跃情况。开发者需要在工程中链接上:SystemConfiguration.framework,libz.dylib,libsqlite3.0.dylib。
|
||||
@@ -0,0 +1,231 @@
|
||||
//
|
||||
// WXApi.h
|
||||
// 所有Api接口
|
||||
//
|
||||
// Created by Wechat on 12-2-28.
|
||||
// Copyright (c) 2012年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "WXApiObject.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
typedef BOOL(^WXGrantReadPasteBoardPermissionCompletion)(void);
|
||||
|
||||
|
||||
#pragma mark - WXApiDelegate
|
||||
/*! @brief 接收并处理来自微信终端程序的事件消息
|
||||
*
|
||||
* 接收并处理来自微信终端程序的事件消息,期间微信界面会切换到第三方应用程序。
|
||||
* WXApiDelegate 会在handleOpenURL:delegate:中使用并触发。
|
||||
*/
|
||||
@protocol WXApiDelegate <NSObject>
|
||||
@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 <NSObject>
|
||||
|
||||
- (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<WXApiDelegate>)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<WXApiDelegate>)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<WXApiDelegate>)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<WXApiLogDelegate>)logDelegate;
|
||||
|
||||
/*! @brief 停止打印log,会清理block或者delegate为空,释放block
|
||||
* @param
|
||||
*/
|
||||
+ (void)stopLog;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// WechatAuthSDK.h
|
||||
// WechatAuthSDK
|
||||
//
|
||||
// Created by 李凯 on 13-11-29.
|
||||
// Copyright (c) 2013年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
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<NSObject>
|
||||
@optional
|
||||
|
||||
- (void)onAuthGotQrcode:(UIImage *)image; //得到二维码
|
||||
- (void)onQrcodeScanned; //二维码被扫描
|
||||
- (void)onAuthFinish:(int)errCode AuthCode:(nullable NSString *)authCode; //成功登录
|
||||
|
||||
@end
|
||||
|
||||
@interface WechatAuthSDK : NSObject{
|
||||
NSString *_sdkVersion;
|
||||
__weak id<WechatAuthAPIDelegate> _delegate;
|
||||
}
|
||||
|
||||
@property(nonatomic, weak, nullable) id<WechatAuthAPIDelegate> 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
|
||||
@@ -0,0 +1,9 @@
|
||||
// WechatOpenSDK.h
|
||||
//
|
||||
// Created by Wechat.
|
||||
// Copyright (c) 2012年 Tencent. All rights reserved.
|
||||
//
|
||||
#import <WechatOpenSDK/WXApi.h>
|
||||
#import <WechatOpenSDK/WXApiObject.h>
|
||||
#import <WechatOpenSDK/WechatAuthSDK.h>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.tencent.WechatOpenSDK</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0.0</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>12.0</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>WechatOpenSDK</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,5 @@
|
||||
framework module WechatOpenSDK {
|
||||
umbrella header "WechatOpenSDK.h"
|
||||
export *
|
||||
link "WechatOpenSDK"
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
//
|
||||
// WXApi.h
|
||||
// 所有Api接口
|
||||
//
|
||||
// Created by Wechat on 12-2-28.
|
||||
// Copyright (c) 2012年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "WXApiObject.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
typedef BOOL(^WXGrantReadPasteBoardPermissionCompletion)(void);
|
||||
|
||||
|
||||
#pragma mark - WXApiDelegate
|
||||
/*! @brief 接收并处理来自微信终端程序的事件消息
|
||||
*
|
||||
* 接收并处理来自微信终端程序的事件消息,期间微信界面会切换到第三方应用程序。
|
||||
* WXApiDelegate 会在handleOpenURL:delegate:中使用并触发。
|
||||
*/
|
||||
@protocol WXApiDelegate <NSObject>
|
||||
@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 <NSObject>
|
||||
|
||||
- (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<WXApiDelegate>)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<WXApiDelegate>)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<WXApiDelegate>)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<WXApiLogDelegate>)logDelegate;
|
||||
|
||||
/*! @brief 停止打印log,会清理block或者delegate为空,释放block
|
||||
* @param
|
||||
*/
|
||||
+ (void)stopLog;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// WechatAuthSDK.h
|
||||
// WechatAuthSDK
|
||||
//
|
||||
// Created by 李凯 on 13-11-29.
|
||||
// Copyright (c) 2013年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
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<NSObject>
|
||||
@optional
|
||||
|
||||
- (void)onAuthGotQrcode:(UIImage *)image; //得到二维码
|
||||
- (void)onQrcodeScanned; //二维码被扫描
|
||||
- (void)onAuthFinish:(int)errCode AuthCode:(nullable NSString *)authCode; //成功登录
|
||||
|
||||
@end
|
||||
|
||||
@interface WechatAuthSDK : NSObject{
|
||||
NSString *_sdkVersion;
|
||||
__weak id<WechatAuthAPIDelegate> _delegate;
|
||||
}
|
||||
|
||||
@property(nonatomic, weak, nullable) id<WechatAuthAPIDelegate> 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
|
||||
@@ -0,0 +1,9 @@
|
||||
// WechatOpenSDK.h
|
||||
//
|
||||
// Created by Wechat.
|
||||
// Copyright (c) 2012年 Tencent. All rights reserved.
|
||||
//
|
||||
#import <WechatOpenSDK/WXApi.h>
|
||||
#import <WechatOpenSDK/WXApiObject.h>
|
||||
#import <WechatOpenSDK/WechatAuthSDK.h>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.tencent.WechatOpenSDK</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0.0</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>12.0</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>WechatOpenSDK</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,5 @@
|
||||
framework module WechatOpenSDK {
|
||||
umbrella header "WechatOpenSDK.h"
|
||||
export *
|
||||
link "WechatOpenSDK"
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// XJWeChatManager.h
|
||||
// XinjiangProject
|
||||
//
|
||||
// 微信分享管理类(手动集成 WechatOpenSDK-NoPay)
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
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
|
||||
@@ -0,0 +1,160 @@
|
||||
//
|
||||
// XJWeChatManager.m
|
||||
// XinjiangProject
|
||||
//
|
||||
// 微信分享管理类
|
||||
//
|
||||
|
||||
#import "XJWeChatManager.h"
|
||||
#import <WechatOpenSDK/WXApi.h>
|
||||
|
||||
@interface XJWeChatManager () <WXApiDelegate>
|
||||
@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
|
||||
@@ -55,7 +55,7 @@
|
||||
if ([HQCommonUtils isLogin]) {
|
||||
//腾讯im
|
||||
#if DEBUG
|
||||
// [self loginImAccount];
|
||||
[self loginImAccount];
|
||||
|
||||
#else
|
||||
[self loginImAccount];
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "SJRotationManager.h"
|
||||
#import "XJWeChatManager.h"
|
||||
#import <SDWebImageWebPCoder/SDWebImageWebPCoder.h>
|
||||
|
||||
@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<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
return [[XJWeChatManager sharedManager] handleOpenURL:url];
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray<id<UIUserActivityRestoring>> * __nullable restorableObjects))restorationHandler {
|
||||
|
||||
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
|
||||
return [[XJWeChatManager sharedManager] handleOpenUniversalLink:userActivity];
|
||||
}
|
||||
return YES;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 - 白屏修复
|
||||
|
||||
@@ -39,6 +39,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
//地图
|
||||
@property (nonatomic, assign) CGFloat Longitude;
|
||||
@property (nonatomic, assign) CGFloat Latitude;
|
||||
@property (nonatomic, copy) NSString * address;//地址描述
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -6,14 +6,12 @@
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIBubbleMessageCell.h>
|
||||
#import <TIMCommon/TUIMessageCell.h>
|
||||
#import "CustomMapMessageCellData.h"
|
||||
#import "CustomTUIHistoryModel.h"
|
||||
|
||||
|
||||
@interface CustomMapMessageCell : UITableViewCell
|
||||
@interface CustomMapMessageCell : TUIBubbleMessageCell
|
||||
|
||||
@property (nonatomic, strong) CustomTUIHistoryModel * model;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -6,224 +6,161 @@
|
||||
//
|
||||
|
||||
#import "CustomMapMessageCell.h"
|
||||
#import "XJWeChatManager.h"
|
||||
#import <SDWebImage/SDImageCache.h>
|
||||
#import<CommonCrypto/CommonDigest.h>
|
||||
|
||||
@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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// XJHealthArchiveDetailViewController.h
|
||||
// XinjiangProject
|
||||
//
|
||||
// 一人一案 - 健康档案查看页
|
||||
//
|
||||
|
||||
#import "XJBaseViewController.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface XJHealthArchiveDetailViewController : XJBaseViewController
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"];
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,325 @@
|
||||
//
|
||||
// XJFirstAidResourceDetailVC.m
|
||||
// XinjiangProject
|
||||
//
|
||||
|
||||
#import "XJFirstAidResourceDetailVC.h"
|
||||
#import "XJFirstAidResourceModel.h"
|
||||
#import <WebKit/WebKit.h>
|
||||
#import <Masonry/Masonry.h>
|
||||
|
||||
@interface XJFirstAidResourceDetailVC () <WKNavigationDelegate>
|
||||
|
||||
@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:@"<!DOCTYPE html>"
|
||||
"<html><head>"
|
||||
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0\">"
|
||||
"<style>"
|
||||
"body { font-family: -apple-system, sans-serif; font-size: 15px; color: #333; "
|
||||
" line-height: 1.7; padding: 0; margin: 0; word-wrap: break-word; }"
|
||||
"img { max-width: 100%%; height: auto; }"
|
||||
"video { max-width: 100%%; height: auto; }"
|
||||
"</style>"
|
||||
"</head><body>%@</body></html>",
|
||||
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
|
||||
@@ -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
|
||||
@@ -0,0 +1,385 @@
|
||||
//
|
||||
// XJSelectContactViewController.m
|
||||
// XinjiangProject
|
||||
//
|
||||
|
||||
#import "XJSelectContactViewController.h"
|
||||
#import "XJPNetAPI.h"
|
||||
|
||||
typedef NS_ENUM(NSUInteger, XJContactTab) {
|
||||
XJContactTabEmployee = 0,
|
||||
XJContactTabExpert = 1,
|
||||
};
|
||||
|
||||
@interface XJSelectContactViewController ()<UITableViewDelegate, UITableViewDataSource>
|
||||
|
||||
@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<NSDictionary *> *dataArray;
|
||||
|
||||
// 选中集合
|
||||
@property (nonatomic, strong) NSMutableSet<NSString *> *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
|
||||
@@ -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"]) {
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// XJFirstAidResourceModel.h
|
||||
// XinjiangProject
|
||||
//
|
||||
// 急救宣教资源 - 详情数据模型
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
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<NSString *> * 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
|
||||
@@ -0,0 +1,10 @@
|
||||
//
|
||||
// XJFirstAidResourceModel.m
|
||||
// XinjiangProject
|
||||
//
|
||||
|
||||
#import "XJFirstAidResourceModel.h"
|
||||
|
||||
@implementation XJFirstAidResourceModel
|
||||
|
||||
@end
|
||||
@@ -63,6 +63,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
////是否评价过
|
||||
@property (nonatomic, assign) BOOL isEveal;
|
||||
|
||||
// 是否从急救员入口进入
|
||||
@property (nonatomic, assign) BOOL isFromFirstAid;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -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 <SDWebImage/SDImageCache.h>
|
||||
|
||||
|
||||
//自定义map
|
||||
@@ -43,7 +50,7 @@
|
||||
|
||||
@class TUIChatDataProvider;
|
||||
|
||||
@interface ChatViewController ()<V2TIMSimpleMsgListener,TUIChatBaseDataProviderDelegate,IMCustomDeleagte,TUIMessageCellDelegate,TUIBaseMessageControllerDelegate,TUICallObserver,V2TIMConversationListener,CLLocationManagerDelegate,UIGestureRecognizerDelegate>
|
||||
@interface ChatViewController ()<V2TIMSimpleMsgListener,TUIChatBaseDataProviderDelegate,IMCustomDeleagte,TUIMessageCellDelegate,TUIBaseMessageControllerDelegate,TUICallObserver,V2TIMConversationListener,CLLocationManagerDelegate,UIGestureRecognizerDelegate,UITableViewDelegate,UITableViewDataSource>
|
||||
|
||||
@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<NSDictionary *> *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
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void(^sendUserSoSLocation)(CLLocationCoordinate2D coordinate);
|
||||
typedef void(^sendUserSoSLocation)(CLLocationCoordinate2D coordinate, NSString *address);
|
||||
|
||||
@interface ChatMapViewController : XJBaseViewController
|
||||
|
||||
|
||||
@@ -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的回调 */
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 12 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 14 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 12 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 18 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 8.2 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 945 B |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "navBackImg.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "navBackImg.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
|
||||
@@ -18,6 +18,14 @@
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>wxb33cd60beda0a212</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
@@ -25,6 +33,8 @@
|
||||
<string>comgooglemaps</string>
|
||||
<string>baidumap</string>
|
||||
<string>iosamap</string>
|
||||
<string>weixin</string>
|
||||
<string>weixinULAPI</string>
|
||||
</array>
|
||||
<key>Localization native development region</key>
|
||||
<string>China</string>
|
||||
@@ -40,5 +50,7 @@
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
<key>UIDesignRequiresCompatibility</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.associated-domains</key>
|
||||
<array>
|
||||
<string>applinks:www.rk-health.com</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||