美文网首页
iOS - 环信小结(待续)

iOS - 环信小结(待续)

作者: Mn_Su | 来源:发表于2017-11-21 13:41 被阅读0次
4ACEDA2C059D7C6A556816D9A59312BC.jpg D7697853C8B24871D4B93DDC39F78848.jpg

零.环信小结,以备后用

一. Cocoapods 集成

 不包含实时语音版本 SDK(HyphenateLite),引用时 #import <HyphenateLite/HyphenateLite.h> 
    pod 'HyphenateLite'

 包含实时语音版本 SDK(Hyphenate),引用时 #import <Hyphenate/Hyphenate.h> 
    pod 'Hyphenate'

二.依赖库相关

    忘了,第一步集成完成后编译下,编译如果成功的话,那就忽略这一步,如果编译失败,去环信官网瞅瞅吧!

三.初始化

1.在全局文件中导入相关文件,或者是需要使用的文件中导入

        //Lite版本
        #import <HyphenateLite/HyphenateLite.h>
        //Full版本
        #import <Hyphenate/Hyphenate.h>

2.在 AppDelegate 中调用SDK方法

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{
// 环信
EMOptions *options = [EMOptions optionsWithAppkey:@"1155170XXXXXXX37#xiubei"];
options.apnsCertName = @"xXXXXei_push";
[[EMClient sharedClient] initializeSDKWithOptions:options];

    [[EaseSDKHelper shareHelper] hyphenateApplication:application
                        didFinishLaunchingWithOptions:launchOptions
                                               appkey:@"115517092XXXX37#xiubei"
                                         apnsCertName:@"xXXXXei_push"
                                          otherConfig:@{kSDKConfigEnableConsoleLogger:[NSNumber numberWithBool:YES]}];

}

      // APP进入后台
    - (void)applicationDidEnterBackground:(UIApplication *)application
    {
        [[EMClient sharedClient] applicationDidEnterBackground:application];
    }
    
    // APP将要从后台返回
    - (void)applicationWillEnterForeground:(UIApplication *)application
    {
        [[EMClient sharedClient] applicationWillEnterForeground:application];
    }

四.登陆且注册环信相关方法

         EMError *error1 = [[EMClient sharedClient] loginWithUsername:_loginView.phoneTF.text password:_loginView.pwdTF.text];
                    
                    if (!error1) {
                        NSLog(@"登录成功");
                        [[EMClient sharedClient].options setIsAutoLogin:YES];
                        
                    }else{
                        NSLog(@"登录失败 : error = %@",error1.errorDescription);
                        EMError *error = [[EMClient sharedClient] registerWithUsername:_loginView.phoneTF.text password:pwd];
                        if (error == nil) {
                            NSLog(@"注册成功");
                        }else{
                            NSLog(@"注册失败 : error = %@",error.errorDescription);
                        }
                        
                    }

** 五.导入EaseUI**

    下载官方demo,导入整个EaseUI目录,[ 环信下载地址 ](http://www.easemob.com/download/im)
615D47FCF21652DD5C8E64843F754B80.jpg

六.导入 ChatDemoHelper 类

七.新建 MSUMessageController 和 MSUMessageChatController两个类,分别继承于 EaseConversationListViewController 和 EaseMessageViewController,相关代码拷贝放入其中

1. MSUMessageController.h 内容

    #import <UIKit/UIKit.h>
    
    @interface MSUMessageController : EaseConversationListViewController
    
    
    @property (strong, nonatomic) NSMutableArray *conversationsArray;
    
    - (void)refresh;
    - (void)refreshDataSource;
    
    - (void)isConnect:(BOOL)isConnect;
    - (void)networkChanged:(EMConnectionState)connectionState;
    
    @end

2. MSUMessageController.m 内容 (其他具体内容视情况而定)

    /// 环信
    #import "MSUMessageChatController.h"
    #import "UserProfileManager.h"
    
    
    @interface MSUMessageController ()<EaseConversationListViewControllerDelegate,EaseConversationListViewControllerDataSource>
    
    @end

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
    
        // 环信
          sender.userInteractionEnabled = YES;
        _messageView.scrollView.contentOffset = CGPointMake(WIDTH*3, 0);
//        self.chatView.hidden = NO;
        self.tableView.frame = CGRectMake(WIDTH*3, 10, WIDTH, HEIGHT-60-37-50);
        self.tableView.backgroundColor = HEXCOLOR(0xf2f2f2);
        [self.messageView.scrollView addSubview:self.tableView];
        self.tableView.hidden = NO;
        self.showRefreshHeader = YES;
        [self tableViewDidTriggerHeaderRefresh];
        self.delegate = self;
        self.dataSource = self;
        [self networkStateView];
//        [self removeEmptyConversationsFromDB];
    }

    #pragma mark - 环信相关
    //- (void)removeEmptyConversationsFromDB
    //{
    //    NSArray *conversations = [[EMClient sharedClient].chatManager getAllConversations];
    //    for (EMConversation *conver in conversations) {
    //        NSLog(@"--%@",conver.conversationId);
    //    }
    //    NSMutableArray *needRemoveConversations;
    //    for (EMConversation *conversation in conversations) {
    //        if (!conversation.latestMessage || (conversation.type == EMConversationTypeChatRoom)) {
    //            if (!needRemoveConversations) {
    //                needRemoveConversations = [[NSMutableArray alloc] initWithCapacity:0];
    //            }
    //            
    //            [needRemoveConversations addObject:conversation];
    //        } else{
    //        
    //        }
    //    }
    //    
    //    if (needRemoveConversations && needRemoveConversations.count > 0) {
    //        [[EMClient sharedClient].chatManager deleteConversations:needRemoveConversations isDeleteMessages:YES completion:nil];
    //    }
    //}
    
    - (UIView *)networkStateView
    {
        if (_networkStateView == nil) {
            _networkStateView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 44)];
            _networkStateView.backgroundColor = [UIColor colorWithRed:255 / 255.0 green:199 / 255.0 blue:199 / 255.0 alpha:0.5];
            
            UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(10, (_networkStateView.frame.size.height - 20) / 2, 20, 20)];
            imageView.image = [UIImage imageNamed:@"messageSendFail"];
            [_networkStateView addSubview:imageView];
            
            UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMaxX(imageView.frame) + 5, 0, _networkStateView.frame.size.width - (CGRectGetMaxX(imageView.frame) + 15), _networkStateView.frame.size.height)];
            label.font = [UIFont systemFontOfSize:15.0];
            label.textColor = [UIColor grayColor];
            label.backgroundColor = [UIColor clearColor];
            label.text = NSLocalizedString(@"network.disconnection", @"Network disconnection");
            [_networkStateView addSubview:label];
        }
        
        return _networkStateView;
    }
    
    #pragma mark - EaseConversationListViewControllerDelegate
    
    - (void)conversationListViewController:(EaseConversationListViewController *)conversationListViewController
                didSelectConversationModel:(id<IConversationModel>)conversationModel
    {
        if (conversationModel) {
            EMConversation *conversation = conversationModel.conversation;
            if (conversation) {
    //            UIViewController *chatController = nil;
                
                self.hidesBottomBarWhenPushed = YES;
                MSUMessageChatController *chatController = [[MSUMessageChatController alloc] initWithConversationChatter:conversation.conversationId conversationType:conversation.type];
                chatController.titStr = conversationModel.title;
                chatController.sellerIconUrl = conversationModel.avatarURLPath;
                chatController.sellerNickName = conversationModel.title;
                [self.navigationController pushViewController:chatController animated:YES];
                self.hidesBottomBarWhenPushed = NO;
            }
            [[NSNotificationCenter defaultCenter] postNotificationName:@"setupUnreadMessageCount" object:nil];
            [self.tableView reloadData];
        }
    }
    
    #pragma mark - EaseConversationListViewControllerDataSource
    
    - (id<IConversationModel>)conversationListViewController:(EaseConversationListViewController *)conversationListViewController
                                        modelForConversation:(EMConversation *)conversation
    {
        NSLog(@"------ %@",conversation);
        EaseConversationModel *model = [[EaseConversationModel alloc] initWithConversation:conversation];
        NSLog(@"------1 %@",model);
        if (model.conversation.type == EMConversationTypeChat) {
            UserProfileEntity *profileEntity = [[UserProfileManager sharedInstance] getUserProfileByUsername:conversation.conversationId];
            if (profileEntity) {
                model.title = profileEntity.nickname == nil ? profileEntity.username : profileEntity.nickname;
                model.avatarURLPath = profileEntity.imageUrl;
            }
            
        }
        return model;
    }
    
    - (NSAttributedString *)conversationListViewController:(EaseConversationListViewController *)conversationListViewController
                    latestMessageTitleForConversationModel:(id<IConversationModel>)conversationModel
    {
        NSMutableAttributedString *attributedStr = [[NSMutableAttributedString alloc] initWithString:@""];
        EMMessage *lastMessage = [conversationModel.conversation latestMessage];
        if (lastMessage) {
            NSString *latestMessageTitle = @"";
            EMMessageBody *messageBody = lastMessage.body;
            switch (messageBody.type) {
                case EMMessageBodyTypeImage:{
                    latestMessageTitle = NSLocalizedString(@"message.image1", @"[image]");
                } break;
                case EMMessageBodyTypeText:{
                    // 表情映射。
                    NSString *didReceiveText = [EaseConvertToCommonEmoticonsHelper
                                                convertToSystemEmoticons:((EMTextMessageBody *)messageBody).text];
                    latestMessageTitle = didReceiveText;
                    if ([lastMessage.ext objectForKey:MESSAGE_ATTR_IS_BIG_EXPRESSION]) {
                        latestMessageTitle = @"[动画表情]";
                    }
                } break;
                case EMMessageBodyTypeVoice:{
                    latestMessageTitle = NSLocalizedString(@"message.voice1", @"[voice]");
                } break;
                case EMMessageBodyTypeLocation: {
                    latestMessageTitle = NSLocalizedString(@"message.location1", @"[location]");
                } break;
                case EMMessageBodyTypeVideo: {
                    latestMessageTitle = NSLocalizedString(@"message.video1", @"[video]");
                } break;
                case EMMessageBodyTypeFile: {
                    latestMessageTitle = NSLocalizedString(@"message.file1", @"[file]");
                } break;
                default: {
                } break;
            }
            
            if (lastMessage.direction == EMMessageDirectionReceive) {
                NSString *from = lastMessage.from;
                if (conversationModel) {
                    from = conversationModel.title;
                }
                latestMessageTitle = [NSString stringWithFormat:@"%@: %@", from, latestMessageTitle];
    
            }
            
            NSDictionary *ext = conversationModel.conversation.ext;
            if (ext && [ext[kHaveUnreadAtMessage] intValue] == kAtAllMessage) {
                latestMessageTitle = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"group.atAll", nil), latestMessageTitle];
                attributedStr = [[NSMutableAttributedString alloc] initWithString:latestMessageTitle];
                [attributedStr setAttributes:@{NSForegroundColorAttributeName : [UIColor colorWithRed:1.0 green:.0 blue:.0 alpha:0.5]} range:NSMakeRange(0, NSLocalizedString(@"group.atAll", nil).length)];
                
            }
            else if (ext && [ext[kHaveUnreadAtMessage] intValue] == kAtYouMessage) {
                latestMessageTitle = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"group.atMe", @"[Somebody @ me]"), latestMessageTitle];
                attributedStr = [[NSMutableAttributedString alloc] initWithString:latestMessageTitle];
                [attributedStr setAttributes:@{NSForegroundColorAttributeName : [UIColor colorWithRed:1.0 green:.0 blue:.0 alpha:0.5]} range:NSMakeRange(0, NSLocalizedString(@"group.atMe", @"[Somebody @ me]").length)];
            }
            else {
                attributedStr = [[NSMutableAttributedString alloc] initWithString:latestMessageTitle];
            }
        }
        
        return attributedStr;
    }
    
    - (NSString *)conversationListViewController:(EaseConversationListViewController *)conversationListViewController
           latestMessageTimeForConversationModel:(id<IConversationModel>)conversationModel
    {
        NSString *latestMessageTime = @"";
        EMMessage *lastMessage = [conversationModel.conversation latestMessage];;
        if (lastMessage) {
            latestMessageTime = [NSDate formattedTimeFromTimeInterval:lastMessage.timestamp];
        }
        
        
        return latestMessageTime;
    }
    
    
    #pragma mark - public
    
    -(void)refresh
    {
        [self refreshAndSortView];
    }
    
    -(void)refreshDataSource
    {
        [self tableViewDidTriggerHeaderRefresh];
    }
    
    - (void)isConnect:(BOOL)isConnect{
        if (!isConnect) {
            self.tableView.tableHeaderView = _networkStateView;
        }
        else{
            self.tableView.tableHeaderView = nil;
        }
        
    }
    
    - (void)networkChanged:(EMConnectionState)connectionState
    {
        if (connectionState == EMConnectionDisconnected) {
            self.tableView.tableHeaderView = _networkStateView;
        }
        else{
            self.tableView.tableHeaderView = nil;
        }
    }
    
3. MSUMessageChatController.h 中的内容

    #import "EaseMessageViewController.h"

    @interface MSUMessageChatController : EaseMessageViewController<EaseMessageViewControllerDelegate, EaseMessageViewControllerDataSource>
    
    /// 返回箭头按钮
    @property (nonatomic , strong) UIButton *arrowBtn;
    /// 相册按钮
    @property (nonatomic , strong) UIButton *photoBtn;
    
    /// 对方昵称
    @property (nonatomic , copy) NSString *sellerNickName;
    /// 对方用户id
    @property (nonatomic , copy) NSString *userId;
    /// 对方头像
    @property (nonatomic , copy) NSString *sellerIconUrl;
    
    /// 发送方头像
    @property (nonatomic , copy) NSString *myselfIcon;
    /// 发送方昵称
    @property (nonatomic , copy) NSString *myselfNickName;
    
    @property (nonatomic , copy) NSString *titStr;
    
    - (void)showMenuViewController:(UIView *)showInView
                      andIndexPath:(NSIndexPath *)indexPath
                       messageType:(EMMessageBodyType)messageType;
    
    @end

4. MSUMessageChatController.m 中的内容

    #import "MSUMessageChatController.h"
    #import "MSUPersonInfoController.h" // 可删除
    
    #import "ChatDemoHelper.h"
    #import "UserProfileManager.h"
    #import "MSUHomeNavView.h"// 可删除
    #import "MSUPrefixHeader.pch"// 可删除
    #import "MSUPathTools.h"// 可删除
    
    #define KNOTIFICATION_LOGINCHANGE @"loginStateChange"
    #define KNOTIFICATIONNAME_DELETEALLMESSAGE @"RemoveAllMessages"
    
    @interface MSUMessageChatController ()<UIAlertViewDelegate,EMClientDelegate>//, EMChooseViewDelegate
    {
        UIMenuItem *_copyMenuItem;
        UIMenuItem *_deleteMenuItem;
        UIMenuItem *_transpondMenuItem;
        UIMenuItem *_recallItem;
    }
    
    @property (nonatomic) BOOL isPlayingAudio;
    
    @property (nonatomic) NSMutableDictionary *emotionDic;
    @property (nonatomic, copy) EaseSelectAtTargetCallback selectedCallback;
    
    @end
    
    @implementation MSUMessageChatController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        
        self.view.backgroundColor = BlackColor;
    //    UIView *bgView = [[UIView alloc] init];
    //    bgView.frame = CGRectMake(0, 60, WIDTH, HEIGHT-60);
    //    bgView.backgroundColor = HEXCOLOR(0xffffff);
    //    [self.view addSubview:bgView];
        
        
        self.showRefreshHeader = YES;
        self.delegate = self;
        self.dataSource = self;
        [self _setupBarButtonItem];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deleteAllMessages:) name:KNOTIFICATIONNAME_DELETEALLMESSAGE object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(exitChat) name:@"ExitChat" object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(insertCallMessage:) name:@"insertCallMessage" object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleCallNotification:) name:@"callOutWithChatter" object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleCallNotification:) name:@"callControllerClose" object:nil];
    //  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(KeyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
    }
    
    //- (void)KeyboardWillChangeFrame:(NSNotification *)note{
    //    [UIView animateWithDuration:0.25 animations:^{
    //        self.tableView.frame = CGRectMake(0, 60, WIDTH, HEIGHT-60);
    //    }];
    //}
    
    - (void)dealloc
    {
       [[NSNotificationCenter defaultCenter] removeObserver:self];
        if (self.conversation.type == EMConversationTypeChatRoom) {
            //退出聊天室,删除会话
            if (self.isJoinedChatroom) {
                NSString *chatter = [self.conversation.conversationId copy];
                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                    EMError *error = nil;
                    [[EMClient sharedClient].roomManager leaveChatroom:chatter error:&error];
                    if (error !=nil) {
                        dispatch_async(dispatch_get_main_queue(), ^{
                            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" message:[NSString stringWithFormat:@"Leave chatroom '%@' failed [%@]", chatter, error.errorDescription] delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
                            [alertView show];
                        });
                    }
                });
            }
            else {
                [[EMClient sharedClient].chatManager deleteConversation:self.conversation.conversationId isDeleteMessages:YES completion:nil];
            }
        }
        
        [[EMClient sharedClient] removeDelegate:self];
    }
    
    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
        if (self.conversation.type == EMConversationTypeGroupChat) {
            NSDictionary *ext = self.conversation.ext;
            if ([[ext objectForKey:@"subject"] length])
            {
                self.title = [ext objectForKey:@"subject"];
            }
            
            if (ext && ext[kHaveUnreadAtMessage] != nil)
            {
                NSMutableDictionary *newExt = [ext mutableCopy];
                [newExt removeObjectForKey:kHaveUnreadAtMessage];
                self.conversation.ext = newExt;
            }
        }
    }
    
    - (void)tableViewDidTriggerHeaderRefresh {
        if ([[ChatDemoHelper shareHelper] isFetchHistoryChange]) {
            NSString *startMessageId = nil;
            if ([self.messsagesSource count] > 0) {
                startMessageId = [(EMMessage *)self.messsagesSource.firstObject messageId];
            }
            
            NSLog(@"startMessageID ------- %@",startMessageId);
            [EMClient.sharedClient.chatManager asyncFetchHistoryMessagesFromServer:self.conversation.conversationId
                                                                  conversationType:self.conversation.type
                                                                    startMessageId:startMessageId
                                                                          pageSize:10
                                                                        complation:^(EMCursorResult *aResult, EMError *aError)
             {
                 [super tableViewDidTriggerHeaderRefresh];
             }];
            
        } else {
            [super tableViewDidTriggerHeaderRefresh];
        }
    }
    
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    //    [self.view endEditing:YES];
    }
    
    - (void)_setupBarButtonItem{
    
        // 左箭头
        self.arrowBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [_arrowBtn setImage:[MSUPathTools showImageWithContentOfFileByName:@"back"] forState:UIControlStateNormal];
    //    _arrowBtn.backgroundColor = [UIColor blueColor];
        [self.view addSubview:_arrowBtn];
        [_arrowBtn makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(self.view.top).offset(27);
            make.left.equalTo(self.view.left).offset(15);
            make.width.equalTo(20);
            make.height.equalTo(30);
        }];
        [self.arrowBtn addTarget:self action:@selector(arrowBtnClick:) forControlEvents:UIControlEventTouchUpInside];
        
        
        if (self.sellerNickName.length > 0) {
            self.titStr = self.sellerNickName;
        }
        // 扫描二维码
        UILabel *scanLab = [[UILabel alloc] init];
        scanLab.text = self.titStr;
        scanLab.font = [UIFont systemFontOfSize:20];
        scanLab.textAlignment = NSTextAlignmentCenter;
        [scanLab setTextColor:WHITECOLOR];
        [self.view addSubview:scanLab];
        [scanLab makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(self.view.top).offset(25);
            make.left.equalTo(self.view.left).offset(WIDTH * 0.5 - 100);
            make.width.equalTo(200);
            make.height.equalTo(34);
        }];
        
        // 相册
        self.photoBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [_photoBtn setImage:[MSUPathTools showImageWithContentOfFileByName:@"delete"] forState:UIControlStateNormal];
        [self.view addSubview:_photoBtn];
        [_photoBtn makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(self.view.top).offset(27);
            make.right.equalTo(self.view.right).offset(-15);
            make.width.equalTo(40);
            make.height.equalTo(30);
        }];
        [self.photoBtn addTarget:self action:@selector(deleteBtnClick:) forControlEvents:UIControlEventTouchUpInside];
    
        self.tableView.frame = CGRectMake(0, 60, WIDTH, HEIGHT-60-49);
    
    }
    
    
    #pragma mark - UIAlertViewDelegate
    
    - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
    {
        if (alertView.cancelButtonIndex != buttonIndex) {
            self.messageTimeIntervalTag = -1;
            [self.conversation deleteAllMessages:nil];
            [self.dataArray removeAllObjects];
            [self.messsagesSource removeAllObjects];
            
            [self.tableView reloadData];
        }
    }
    
    #pragma mark - EaseMessageViewControllerDelegate
    
    - (UITableViewCell *)messageViewController:(UITableView *)tableView
                           cellForMessageModel:(id<IMessageModel>)messageModel
    {
        NSDictionary *ext = messageModel.message.ext;
        if ([ext objectForKey:@"em_recall"]) {
            NSString *TimeCellIdentifier = [EaseMessageTimeCell cellIdentifier];
            EaseMessageTimeCell *recallCell = (EaseMessageTimeCell *)[tableView dequeueReusableCellWithIdentifier:TimeCellIdentifier];
            
            if (recallCell == nil) {
                recallCell = [[EaseMessageTimeCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TimeCellIdentifier];
                recallCell.selectionStyle = UITableViewCellSelectionStyleNone;
            }
            
            EMTextMessageBody *body = (EMTextMessageBody*)messageModel.message.body;
            recallCell.title = body.text;
            return recallCell;
        }
        return nil;
    }
    
    - (CGFloat)messageViewController:(EaseMessageViewController *)viewController
               heightForMessageModel:(id<IMessageModel>)messageModel
                       withCellWidth:(CGFloat)cellWidth
    {
        NSDictionary *ext = messageModel.message.ext;
        if ([ext objectForKey:@"em_recall"]) {
            return self.timeCellHeight;
        }
        return 0;
    }
    
    - (BOOL)messageViewController:(EaseMessageViewController *)viewController
       canLongPressRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return YES;
    }
    
    - (BOOL)messageViewController:(EaseMessageViewController *)viewController
       didLongPressRowAtIndexPath:(NSIndexPath *)indexPath
    {
        id object = [self.dataArray objectAtIndex:indexPath.row];
        if (![object isKindOfClass:[NSString class]]) {
            EaseMessageCell *cell = (EaseMessageCell *)[self.tableView cellForRowAtIndexPath:indexPath];
            if ([cell isKindOfClass:[EaseMessageCell class]]) {
                [cell becomeFirstResponder];
                self.menuIndexPath = indexPath;
                [self showMenuViewController:cell.bubbleView andIndexPath:indexPath messageType:cell.model.bodyType];
            }
        }
        return YES;
    }
    
    - (void)messageViewController:(EaseMessageViewController *)viewController
      didSelectAvatarMessageModel:(id<IMessageModel>)messageModel
    {
    //    self.hidesBottomBarWhenPushed = YES;
    //    MSUPersonInfoController *userprofile = [[MSUPersonInfoController alloc] init];
    //    userprofile.userID = self.userId;
    //    [self.navigationController pushViewController:userprofile animated:YES];
    }
    
    - (void)messageViewController:(EaseMessageViewController *)viewController
                   selectAtTarget:(EaseSelectAtTargetCallback)selectedCallback
    {
        _selectedCallback = selectedCallback;
        EMGroup *chatGroup = nil;
        NSArray *groupArray = [[EMClient sharedClient].groupManager getJoinedGroups];
        for (EMGroup *group in groupArray) {
            if ([group.groupId isEqualToString:self.conversation.conversationId]) {
                chatGroup = group;
                break;
            }
        }
        
        if (chatGroup == nil) {
            chatGroup = [EMGroup groupWithId:self.conversation.conversationId];
        }
        
        if (chatGroup) {
            if (!chatGroup.occupants) {
                __weak MSUMessageChatController* weakSelf = self;
                [self showHudInView:self.view hint:@"Fetching group members..."];
                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                    EMError *error = nil;
                    EMGroup *group = [[EMClient sharedClient].groupManager getGroupSpecificationFromServerWithId:chatGroup.groupId error:&error];
                    dispatch_async(dispatch_get_main_queue(), ^{
                        __strong MSUMessageChatController *strongSelf = weakSelf;
                        if (strongSelf) {
                            [strongSelf hideHud];
                            if (error) {
                                UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" message:[NSString stringWithFormat:@"Fetching group members failed [%@]", error.errorDescription] delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
                                [alertView show];
                            }
                            else {
                                NSMutableArray *members = [group.occupants mutableCopy];
                                NSString *loginUser = [EMClient sharedClient].currentUsername;
                                if (loginUser) {
                                    [members removeObject:loginUser];
                                }
                                if (![members count]) {
                                    if (strongSelf.selectedCallback) {
                                        strongSelf.selectedCallback(nil);
                                    }
                                    return;
                                }
    //                            ContactSelectionViewController *selectController = [[ContactSelectionViewController alloc] initWithContacts:members];
    //                            selectController.mulChoice = NO;
    //                            selectController.delegate = self;
    //                            [self.navigationController pushViewController:selectController animated:YES];
                            }
                        }
                    });
                });
            }
            else {
                NSMutableArray *members = [chatGroup.occupants mutableCopy];
                NSString *loginUser = [EMClient sharedClient].currentUsername;
                if (loginUser) {
                    [members removeObject:loginUser];
                }
                if (![members count]) {
                    if (_selectedCallback) {
                        _selectedCallback(nil);
                    }
                    return;
                }
    //            ContactSelectionViewController *selectController = [[ContactSelectionViewController alloc] initWithContacts:members];
    //            selectController.mulChoice = NO;
    //            selectController.delegate = self;
    //            [self.navigationController pushViewController:selectController animated:YES];
            }
        }
    }
    
    #pragma mark - EaseMessageViewControllerDataSource
    
    - (id<IMessageModel>)messageViewController:(EaseMessageViewController *)viewController
                               modelForMessage:(EMMessage *)message
    {
        id<IMessageModel> model = nil;
        model = [[EaseMessageModel alloc] initWithMessage:message];
        if (model.isSender) {
            model.nickname = @"我";
            if (self.myselfIcon.length > 0) {
                model.avatarImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://psource.showbuy100.com%@",self.myselfIcon]]]];
            } else{
                model.avatarImage = [UIImage imageNamed:@"search-headbig"];
            }
    
    
        } else{
            if (self.sellerIconUrl.length > 0) {
                model.avatarImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://psource.showbuy100.com%@",self.sellerIconUrl]]]];
            } else{
                model.avatarImage = [UIImage imageNamed:@"search-headbig"];
            }
    
            model.nickname = self.sellerNickName.length > 0 ? self.sellerNickName : @"对方";
    //        UserProfileEntity *profileEntity = [[UserProfileManager sharedInstance] getUserProfileByUsername:model.nickname];
    //        if (profileEntity) {
    //            model.avatarURLPath = [NSString stringWithFormat:@"http://psource.showbuy100.com%@",self.sellerIconUrl];
    //            model.nickname = self.sellerNickName;
    //        }
        }
    
        model.failImageName = @"imageDownloadFail";
        return model;
    }
    
    - (NSArray*)emotionFormessageViewController:(EaseMessageViewController *)viewController
    {
        NSMutableArray *emotions = [NSMutableArray array];
        for (NSString *name in [EaseEmoji allEmoji]) {
            EaseEmotion *emotion = [[EaseEmotion alloc] initWithName:@"" emotionId:name emotionThumbnail:name emotionOriginal:name emotionOriginalURL:@"" emotionType:EMEmotionDefault];
            [emotions addObject:emotion];
        }
        EaseEmotion *temp = [emotions objectAtIndex:0];
        EaseEmotionManager *managerDefault = [[EaseEmotionManager alloc] initWithType:EMEmotionDefault emotionRow:3 emotionCol:7 emotions:emotions tagImage:[UIImage imageNamed:temp.emotionId]];
        
        NSMutableArray *emotionGifs = [NSMutableArray array];
        _emotionDic = [NSMutableDictionary dictionary];
        NSArray *names = @[@"icon_002",@"icon_007",@"icon_010",@"icon_012",@"icon_013",@"icon_018",@"icon_019",@"icon_020",@"icon_021",@"icon_022",@"icon_024",@"icon_027",@"icon_029",@"icon_030",@"icon_035",@"icon_040"];
        int index = 0;
        for (NSString *name in names) {
            index++;
            EaseEmotion *emotion = [[EaseEmotion alloc] initWithName:[NSString stringWithFormat:@"[示例%d]",index] emotionId:[NSString stringWithFormat:@"em%d",(1000 + index)] emotionThumbnail:[NSString stringWithFormat:@"%@_cover",name] emotionOriginal:[NSString stringWithFormat:@"%@",name] emotionOriginalURL:@"" emotionType:EMEmotionGif];
            [emotionGifs addObject:emotion];
            [_emotionDic setObject:emotion forKey:[NSString stringWithFormat:@"em%d",(1000 + index)]];
        }
        EaseEmotionManager *managerGif= [[EaseEmotionManager alloc] initWithType:EMEmotionGif emotionRow:2 emotionCol:4 emotions:emotionGifs tagImage:[UIImage imageNamed:@"icon_002_cover"]];
        
        return @[managerDefault,managerGif];
    }
    
    - (BOOL)isEmotionMessageFormessageViewController:(EaseMessageViewController *)viewController
                                        messageModel:(id<IMessageModel>)messageModel
    {
        BOOL flag = NO;
        if ([messageModel.message.ext objectForKey:MESSAGE_ATTR_IS_BIG_EXPRESSION]) {
            return YES;
        }
        return flag;
    }
    
    - (EaseEmotion*)emotionURLFormessageViewController:(EaseMessageViewController *)viewController
                                          messageModel:(id<IMessageModel>)messageModel
    {
        NSString *emotionId = [messageModel.message.ext objectForKey:MESSAGE_ATTR_EXPRESSION_ID];
        EaseEmotion *emotion = [_emotionDic objectForKey:emotionId];
        if (emotion == nil) {
            emotion = [[EaseEmotion alloc] initWithName:@"" emotionId:emotionId emotionThumbnail:@"" emotionOriginal:@"" emotionOriginalURL:@"" emotionType:EMEmotionGif];
        }
        return emotion;
    }
    
    - (NSDictionary*)emotionExtFormessageViewController:(EaseMessageViewController *)viewController
                                            easeEmotion:(EaseEmotion*)easeEmotion
    {
        return @{MESSAGE_ATTR_EXPRESSION_ID:easeEmotion.emotionId,MESSAGE_ATTR_IS_BIG_EXPRESSION:@(YES)};
    }
    
    - (void)messageViewControllerMarkAllMessagesAsRead:(EaseMessageViewController *)viewController
    {
        [[NSNotificationCenter defaultCenter] postNotificationName:@"setupUnreadMessageCount" object:nil];
    }
    
    #pragma mark - EaseMob
    
    #pragma mark - EMClientDelegate
    
    - (void)userAccountDidLoginFromOtherDevice
    {
        if ([self.imagePicker.mediaTypes count] > 0 && [[self.imagePicker.mediaTypes objectAtIndex:0] isEqualToString:(NSString *)kUTTypeMovie]) {
            [self.imagePicker stopVideoCapture];
        }
    }
    
    - (void)userAccountDidRemoveFromServer
    {
        if ([self.imagePicker.mediaTypes count] > 0 && [[self.imagePicker.mediaTypes objectAtIndex:0] isEqualToString:(NSString *)kUTTypeMovie]) {
            [self.imagePicker stopVideoCapture];
        }
    }
    
    - (void)userDidForbidByServer
    {
        if ([self.imagePicker.mediaTypes count] > 0 && [[self.imagePicker.mediaTypes objectAtIndex:0] isEqualToString:(NSString *)kUTTypeMovie]) {
            [self.imagePicker stopVideoCapture];
        }
    }
    
    #pragma mark - EMChatManagerDelegate
    
    - (void)messagesDidRecall:(NSArray *)aMessages
    {
        for (EMMessage *msg in aMessages) {
            if (![self.conversation.conversationId isEqualToString:msg.conversationId]){
                continue;
            }
            
            NSString *text;
            if ([msg.from isEqualToString:[EMClient sharedClient].currentUsername]) {
                text = [NSString stringWithFormat:NSLocalizedString(@"message.recall", @"You recall a message")];
            } else {
                text = [NSString stringWithFormat:NSLocalizedString(@"message.recallByOthers", @"%@ recall a message"),msg.from];
            }
            
            [self _recallWithMessage:msg text:text isSave:NO];
        }
    }
    
    - (void)arrowBtnClick:(UIButton *)sender{
        [[EMClient sharedClient].chatManager removeDelegate:self];
        [[EMClient sharedClient].roomManager removeDelegate:self];
        [[ChatDemoHelper shareHelper] setChatVC:nil];
        
        if (self.deleteConversationIfNull) {
            //判断当前会话是否为空,若符合则删除该会话
            EMMessage *message = [self.conversation latestMessage];
            if (message == nil) {
                [[EMClient sharedClient].chatManager deleteConversation:self.conversation.conversationId isDeleteMessages:NO completion:nil];
            }
        }
        
        [self.navigationController popViewControllerAnimated:YES];
    
    }
    
    - (void)deleteBtnClick:(UIButton *)sender{
        if (self.dataArray.count == 0) {
    //        [self showHint:NSLocalizedString(@"message.noMessage", @"no messages")];
            [self showHint:@"暂无任何消息"];
    
            return;
        }
        
        if ([sender isKindOfClass:[NSNotification class]]) {
            NSString *groupId = (NSString *)[(NSNotification *)sender object];
            BOOL isDelete = [groupId isEqualToString:self.conversation.conversationId];
            if (self.conversation.type != EMConversationTypeChat && isDelete) {
                self.messageTimeIntervalTag = -1;
                [self.conversation deleteAllMessages:nil];
                [self.messsagesSource removeAllObjects];
                [self.dataArray removeAllObjects];
                
                [self.tableView reloadData];
                [self showHint:NSLocalizedString(@"message.noMessage", @"no messages")];
            }
        }
        else if ([sender isKindOfClass:[UIButton class]]){
            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"prompt", @"Prompt") message:NSLocalizedString(@"sureToDelete", @"please make sure to delete") delegate:self cancelButtonTitle:NSLocalizedString(@"cancel", @"Cancel") otherButtonTitles:NSLocalizedString(@"sure", @"确定"), nil];
            [alertView show];
        }
    
    }
    
    - (void)recallMenuAction:(id)sender
    {
        if (self.menuIndexPath && self.menuIndexPath.row > 0) {
            __weak typeof(self) weakSelf = self;
            id<IMessageModel> model = [self.dataArray objectAtIndex:self.menuIndexPath.row];
            [[EMClient sharedClient].chatManager recallMessage:model.message
                                                    completion:^(EMMessage *aMessage, EMError *aError) {
                                                        if (!aError) {
                                                            [weakSelf _recallWithMessage:aMessage text:NSLocalizedString(@"message.recall", @"You recall a message") isSave:YES];
                                                        } else {
                                                            [weakSelf showHint:[NSString stringWithFormat:NSLocalizedString(@"recallFailed", @"Recall failed:%@"), aError.errorDescription]];
                                                        }
                                                        weakSelf.menuIndexPath = nil;
                                                    }];
        }
    }
    
    //- (void)transpondMenuAction:(id)sender
    //{
    //    if (self.menuIndexPath && self.menuIndexPath.row > 0) {
    //        id<IMessageModel> model = [self.dataArray objectAtIndex:self.menuIndexPath.row];
    ////        ContactListSelectViewController *listViewController = [[ContactListSelectViewController alloc] initWithNibName:nil bundle:nil];
    ////        listViewController.messageModel = model;
    ////        [listViewController tableViewDidTriggerHeaderRefresh];
    ////        [self.navigationController pushViewController:listViewController animated:YES];
    //    }
    //    self.menuIndexPath = nil;
    //}
    
    - (void)copyMenuAction:(id)sender
    {
        UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
        if (self.menuIndexPath && self.menuIndexPath.row > 0) {
            id<IMessageModel> model = [self.dataArray objectAtIndex:self.menuIndexPath.row];
            pasteboard.string = model.text;
        }
        
        self.menuIndexPath = nil;
    }
    
    - (void)deleteMenuAction:(id)sender
    {
        if (self.menuIndexPath && self.menuIndexPath.row > 0) {
            id<IMessageModel> model = [self.dataArray objectAtIndex:self.menuIndexPath.row];
            NSMutableIndexSet *indexs = [NSMutableIndexSet indexSetWithIndex:self.menuIndexPath.row];
            NSMutableArray *indexPaths = [NSMutableArray arrayWithObjects:self.menuIndexPath, nil];
            
            [self.conversation deleteMessageWithId:model.message.messageId error:nil];
            [self.messsagesSource removeObject:model.message];
            
            if (self.menuIndexPath.row - 1 >= 0) {
                id nextMessage = nil;
                id prevMessage = [self.dataArray objectAtIndex:(self.menuIndexPath.row - 1)];
                if (self.menuIndexPath.row + 1 < [self.dataArray count]) {
                    nextMessage = [self.dataArray objectAtIndex:(self.menuIndexPath.row + 1)];
                }
                if ((!nextMessage || [nextMessage isKindOfClass:[NSString class]]) && [prevMessage isKindOfClass:[NSString class]]) {
                    [indexs addIndex:self.menuIndexPath.row - 1];
                    [indexPaths addObject:[NSIndexPath indexPathForRow:(self.menuIndexPath.row - 1) inSection:0]];
                }
            }
            
            [self.dataArray removeObjectsAtIndexes:indexs];
            [self.tableView beginUpdates];
            [self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
            [self.tableView endUpdates];
            
            if ([self.dataArray count] == 0) {
                self.messageTimeIntervalTag = -1;
            }
        }
        
        self.menuIndexPath = nil;
    }
    
    #pragma mark - notification
    - (void)exitChat
    {
        [self.navigationController popToViewController:self animated:NO];
        [self.navigationController popViewControllerAnimated:YES];
    }
    
    - (void)insertCallMessage:(NSNotification *)notification
    {
        id object = notification.object;
        if (object) {
            EMMessage *message = (EMMessage *)object;
            [self addMessageToDataSource:message progress:nil];
            [[EMClient sharedClient].chatManager importMessages:@[message] completion:nil];
        }
    }
    
    - (void)handleCallNotification:(NSNotification *)notification
    {
        id object = notification.object;
        if ([object isKindOfClass:[NSDictionary class]]) {
            //开始call
            self.isViewDidAppear = NO;
        } else {
            //结束call
            self.isViewDidAppear = YES;
        }
    }
    
    #pragma mark - private
    
    - (void)showMenuViewController:(UIView *)showInView
                      andIndexPath:(NSIndexPath *)indexPath
                       messageType:(EMMessageBodyType)messageType
    {
        if (self.menuController == nil) {
            self.menuController = [UIMenuController sharedMenuController];
        }
        
        if (_deleteMenuItem == nil) {
            _deleteMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"delete", @"Delete") action:@selector(deleteMenuAction:)];
        }
        
        if (_copyMenuItem == nil) {
            _copyMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"copy", @"Copy") action:@selector(copyMenuAction:)];
        }
        
    //    if (_transpondMenuItem == nil) {
    //        _transpondMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"transpond", @"Transpond") action:@selector(transpondMenuAction:)];
    //    }
        
        if (_recallItem == nil) {
            _recallItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"recall", @"Recall") action:@selector(recallMenuAction:)];
        }
        
        NSMutableArray *items = [NSMutableArray array];
        
        if (messageType == EMMessageBodyTypeText) {
            [items addObject:_copyMenuItem];
    //        [items addObject:_transpondMenuItem];
            [items addObject:_deleteMenuItem];
        } else if (messageType == EMMessageBodyTypeImage || messageType == EMMessageBodyTypeVideo) {
    //        [items addObject:_transpondMenuItem];
            [items addObject:_deleteMenuItem];
        } else {
            [items addObject:_deleteMenuItem];
        }
        
        id<IMessageModel> model = [self.dataArray objectAtIndex:self.menuIndexPath.row];
        if (model.isSender) {
            [items addObject:_recallItem];
        }
        
        [self.menuController setMenuItems:items];
        [self.menuController setTargetRect:showInView.frame inView:showInView.superview];
        [self.menuController setMenuVisible:YES animated:YES];
    }
    
    - (void)_recallWithMessage:(EMMessage *)msg text:(NSString *)text isSave:(BOOL)isSave
    {
        EMMessage *message = [EaseSDKHelper sendTextMessage:text to:msg.conversationId messageType:msg.chatType messageExt:@{@"em_recall":@(YES)}];
        message.isRead = YES;
        [message setTimestamp:msg.timestamp];
        [message setLocalTime:msg.localTime];
        id<IMessageModel> newModel = [[EaseMessageModel alloc] initWithMessage:message];
        __block NSUInteger index = NSNotFound;
        [self.dataArray enumerateObjectsUsingBlock:^(EaseMessageModel *model, NSUInteger idx, BOOL *stop){
            if ([model conformsToProtocol:@protocol(IMessageModel)]) {
                if ([msg.messageId isEqualToString:model.message.messageId])
                {
                    index = idx;
                    *stop = YES;
                }
            }
        }];
        if (index != NSNotFound)
        {
            __block NSUInteger sourceIndex = NSNotFound;
            [self.messsagesSource enumerateObjectsUsingBlock:^(EMMessage *message, NSUInteger idx, BOOL *stop){
                if ([message isKindOfClass:[EMMessage class]]) {
                    if ([msg.messageId isEqualToString:message.messageId])
                    {
                        sourceIndex = idx;
                        *stop = YES;
                    }
                }
            }];
            if (sourceIndex != NSNotFound) {
                [self.messsagesSource replaceObjectAtIndex:sourceIndex withObject:newModel.message];
            }
            [self.dataArray replaceObjectAtIndex:index withObject:newModel];
            [self.tableView reloadData];
        }
        
        if (isSave) {
            [self.conversation insertMessage:message error:nil];
        }
    }
    
    #pragma mark - EMChooseViewDelegate
    
    //- (BOOL)viewController:(EMChooseViewController *)viewController didFinishSelectedSources:(NSArray *)selectedSources
    //{
    //    if ([selectedSources count]) {
    //        EaseAtTarget *target = [[EaseAtTarget alloc] init];
    //        target.userId = selectedSources.firstObject;
    //        UserProfileEntity *profileEntity = [[UserProfileManager sharedInstance] getUserProfileByUsername:target.userId];
    //        if (profileEntity) {
    //            target.nickname = profileEntity.nickname == nil ? profileEntity.username : profileEntity.nickname;
    //        }
    //        if (_selectedCallback) {
    //            _selectedCallback(target);
    //        }
    //    }
    //    else {
    //        if (_selectedCallback) {
    //            _selectedCallback(nil);
    //        }
    //    }
    //    return YES;
    //}
    //
    //- (void)viewControllerDidSelectBack:(EMChooseViewController *)viewController
    //{
    //    if (_selectedCallback) {
    //        _selectedCallback(nil);
    //    }
    //}

八.环信消息推送

1.[ 开发者账号证书配置 ](http://www.jianshu.com/p/eb8d9493bc9d)

2.[ 上传推送证书到环信 ](http://docs.easemob.com/im/300iosclientintegration/apns)

3.在 didFinishLaunchingWithOptions 中 初始化环信及环信推送

    /*  EM 环信 */
    - (void)setUpEMSomething{
        EMOptions *options = [EMOptions optionsWithAppkey:@"115517XXXX115537#xiubei"];
        options.apnsCertName = @"xiubei_develop";
        [[EMClient sharedClient] initializeSDKWithOptions:options];
        
        EMPushOptions *emoptions = [[EMClient sharedClient] pushOptions];
        emoptions.displayStyle = EMPushDisplayStyleMessageSummary;
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [[EMClient sharedClient] updatePushOptionsToServer];
        });
        
        [[EaseSDKHelper shareHelper] hyphenateApplication:application
                            didFinishLaunchingWithOptions:launchOptions
                                                   appkey:@"115517XXXX115537#xiubei"
                                             apnsCertName:@"xiubei_develop"
                                              otherConfig:@{kSDKConfigEnableConsoleLogger:[NSNumber numberWithBool:YES]}];
        
        [[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];
    }

4.注意点

    1)未上线前,通过更改 options.apnsCertName ,来进行上线和未上线的区别(证书名字在请看第2步 - 环信证书上传步骤)

    2)推送显示消息内容 是 "一条新消息"还是消息详情内容,设置属性 emoptions.displayStyle

            EMPushOptions *emoptions = [[EMClient sharedClient] pushOptions];
            emoptions.displayStyle = EMPushDisplayStyleMessageSummary;
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                [[EMClient sharedClient] updatePushOptionsToServer];
            });

九.问题小结

1. 自定义 列表视图的 tableview 高度,根据自己需求调整!

    方法:- (void)chatToolbarDidChangeFrameToHeight:(CGFloat)toHeight 
BB7AE49930F859B4C63B1BDA7D9B29AB.jpg
2.messageChatController 即私信对话框页面,不能实现 scrollView代理隐藏键盘,不然聊天记录超过半屏后会造成屏幕卡住,私聊页面无法使用情况!

3.在 tableViewDidTriggerHeaderRefresh 方法里面,对接服务器,获取聊天列表头像和昵称!


4.在 EaseConversationListViewController 中的 tableViewDidTriggerHeaderRefresh 方法中请求服务器,获取会话列表相关信息!

相关文章

网友评论

      本文标题:iOS - 环信小结(待续)

      本文链接:https://www.haomeiwen.com/subject/vsxwvxtx.html