QMChatRoomViewController.m 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322
  1. //
  2. // QMChatRoomViewController.m
  3. // IMSDK-OC
  4. //
  5. // Created by HCF on 16/3/9.
  6. // Copyright © 2016年 HCF. All rights reserved.
  7. //
  8. #import "QMChatRoomViewController.h"
  9. #import "QMChatRoomInputView.h"
  10. #import "TCMessageManagerFaceView.h"
  11. #import "QMChatRoomMoreView.h"
  12. #import <QMLineSDK/QMLineSDK.h>
  13. #import <QMLineSDK/QMLineSDK.h>
  14. #import "QMRecordIndicatorView.h"
  15. #import "MJRefresh.h"
  16. #import "TZImagePickerController.h"
  17. #import "QMChatRoomShowImageController.h"
  18. #import "QMProfileManager.h"
  19. #import "QMTapGestureRecognizer.h"
  20. #import "QMChatRoomGuestBookViewController.h"
  21. #import "QMChatRoomRobotCell.h"
  22. #import "QMChatRoomTextCell.h"
  23. #import "QMChatRoomVoiceCell.h"
  24. #import "QMChatRoomImageCell.h"
  25. #import "QMChatRoomInvestigateCell.h"
  26. #import "QMChatRoomFileCell.h"
  27. #import "QMChatRoomIframeCell.h"
  28. #import "QMChatRoomCellFactory.h"
  29. #import "QMChatRoomRichTextCell.h"
  30. #import "QMChatRoomNoteCell.h"
  31. #import "QMChatRoomMp3Cell.h"
  32. #import "QMChatRoomCardCell.h"
  33. #import "QMAudioPlayer.h"
  34. #import "QMAudioRecorder.h"
  35. #import "SJVoiceTransform.h"
  36. //new
  37. #import "QMChatTileView.h"
  38. #import "QMFileManagerController.h"
  39. #import "QMAlert.h"
  40. #import "QMTextAttachment.h"
  41. #import "NSAttributedString+QMEmojiExtension.h"
  42. #import <Photos/Photos.h>
  43. #import "Reachability.h"
  44. #import "QMTextModel.h"
  45. /**
  46. 在线客服聊天界面
  47. */
  48. @interface QMChatRoomViewController ()<UITableViewDataSource, UITableViewDelegate, UITextViewDelegate, TCMessageManagerFaceViewDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate,UIAlertViewDelegate, AVAudioRecorderDelegate, QMKServiceDelegate, QMAudioRecorderDelegate> {
  49. NSMutableArray * _dataArray;
  50. NSArray * _investigateArray;
  51. int _dataNum;
  52. CGFloat _keyboardHigh;
  53. QMChatTileView *_titleView;
  54. NSTimer *breakTimer;
  55. NSTimer *breakTipTimer;
  56. NSTimer *backStatus;
  57. CGFloat _navHeight;
  58. NSString *_titleViewText;
  59. }
  60. @property (nonatomic, assign)float upProgress;
  61. @end
  62. @implementation QMChatRoomViewController
  63. #pragma mark - 生命周期
  64. // 注册通知
  65. -(instancetype)init {
  66. self = [super init];
  67. if (self) {
  68. // 建议使用willshow和willhide
  69. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardFrameChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
  70. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getNewReload:) name:CHATMSG_RELOAD object:nil];
  71. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(robotAction) name:ROBOT_SERVICE object:nil];
  72. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customOnline) name:CUSTOMSRV_ONLINE object:nil];
  73. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customOffline) name:CUSTOMSRV_OFFLINE object:nil];
  74. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customClaim) name:CUSTOMSRV_CLAIM object:nil];
  75. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customFinish) name:CUSTOMSRV_FINISH object:nil];
  76. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customQueue:) name:CUSTOMSRV_QUEUENUM object:nil];
  77. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customAgentMessage:) name:CUSTOMSRV_AGENT object:nil];
  78. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customInvestigate) name:CUSTOMSRV_INVESTIGATE object:nil];
  79. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customVIP) name:CUSTOMSRV_VIP object:nil];
  80. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customLeavemsg:) name:CUSTOMSRV_LEAVEMSG object:nil];
  81. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeCustomStatus) name:CUSTOMSRV_IMPORTING object:nil];
  82. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cbangeDrowMessageStatus:) name:CUSTOMSRV_DRAWMESSAGE object:nil];
  83. }
  84. return self;
  85. }
  86. // 基本配置
  87. - (void)viewWillAppear:(BOOL)animated {
  88. self.navigationController.navigationBarHidden = NO;
  89. self.navigationController.navigationBar.barTintColor = [UIColor colorWithRed:246/255.0 green:248/255.0 blue:249/255.0 alpha:1.0];
  90. self.navigationController.navigationBar.tintColor = [UIColor colorWithRed:13/255.0 green:139/255.0 blue:249/255.0 alpha:1];
  91. [self.navigationController.navigationBar setTranslucent:NO];
  92. self.navigationController.interactivePopGestureRecognizer.delaysTouchesBegan = NO;
  93. }
  94. /**
  95. 开始会话
  96. peer: 技能组id 必传
  97. params: @{@"agent":@"8000"} 专属坐席
  98. @{@"customField":@{@"姓名":@"李三"}} 自定义字段
  99. */
  100. - (void)viewDidAppear:(BOOL)animated {
  101. if (self.isPush == NO) {
  102. __weak QMChatRoomViewController * myChatView = self;
  103. if (self.isOpenSchedule) {
  104. NSLog(@"sdk走日程管理方法");
  105. [QMConnect sdkBeginNewChatSessionSchedule: self.scheduleId processId: self.processId currentNodeId: self.currentNodeId entranceId: self.entranceId params: @{@"":@""} successBlock:^(BOOL remark, NSString *str) {
  106. NSLog(@"开始会话成功");
  107. dispatch_async(dispatch_get_main_queue(), ^{
  108. // 是否启动了评价功能
  109. if (remark == NO) {
  110. myChatView.addView.evaluateBtn.hidden = YES;
  111. myChatView.addView.evaluateLabel.hidden = YES;
  112. }else {
  113. myChatView.addView.evaluateBtn.hidden = NO;
  114. myChatView.addView.evaluateLabel.hidden = NO;
  115. }
  116. });
  117. } failBlock:^(NSString *str) {
  118. NSLog(@"开始会话失败, %@" , str);
  119. }];
  120. }else{
  121. NSLog(@"sdk走技能组方法");
  122. [QMConnect sdkBeginNewChatSession:self.peerId params:@{@"customField":@{@"1111":@"2222",@"3333":@"4444"},@"agent":@"8129"} successBlock:^(BOOL remark, NSString *str) {
  123. NSLog(@"开始会话成功");
  124. dispatch_async(dispatch_get_main_queue(), ^{
  125. // 是否启动了评价功能
  126. if (remark == NO) {
  127. myChatView.addView.evaluateBtn.hidden = YES;
  128. myChatView.addView.evaluateLabel.hidden = YES;
  129. }else {
  130. myChatView.addView.evaluateBtn.hidden = NO;
  131. myChatView.addView.evaluateLabel.hidden = NO;
  132. }
  133. });
  134. } failBlock:^(NSString *str) {
  135. NSLog(@"开始会话失败, %@" , str);
  136. }];
  137. }
  138. self.isPush = YES;
  139. }
  140. }
  141. - (void)viewDidLoad {
  142. [super viewDidLoad];
  143. self.view.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1];
  144. [self createUI];
  145. //判断是否是机器人(默认是关闭的)
  146. if (![QMConnect allowRobot]) {
  147. // [self insertCardInfoMessage];
  148. }
  149. /**
  150. 创建文件管理类
  151. name: 可随便填写
  152. password: 可随便填写
  153. */
  154. QMProfileManager *manger = [QMProfileManager sharedInstance];
  155. [manger loadProfile:@"name" password:@"123456"];
  156. // 网络监控
  157. Reachability *hostReach = [Reachability reachabilityForInternetConnection];
  158. hostReach.reachableBlock = ^(Reachability *reachability) {
  159. dispatch_async(dispatch_get_main_queue(), ^{
  160. // 网络连接
  161. });
  162. };
  163. hostReach.unreachableBlock = ^(Reachability *reachability) {
  164. dispatch_async(dispatch_get_main_queue(), ^{
  165. // 网络断开
  166. });
  167. };
  168. [hostReach startNotifier];
  169. [self getInvestigateData];
  170. _dataArray = [[NSMutableArray alloc]init];
  171. if (_dataNum == 0) {
  172. _dataNum = 10;
  173. }
  174. [self getData];
  175. [self.chatTableView reloadData];
  176. dispatch_async(dispatch_get_main_queue(), ^{
  177. [self scrollToEnd];
  178. });
  179. [self createNSTimer];
  180. self.heightCaches = [NSMutableDictionary dictionary];
  181. }
  182. - (void)viewWillDisappear:(BOOL)animated {
  183. if ([self.chatInputView.inputView isFirstResponder]) {
  184. [self.chatInputView.inputView resignFirstResponder];
  185. self.chatInputView.inputView.inputView = nil;
  186. }
  187. [self.navigationController.navigationBar setTranslucent:YES];
  188. }
  189. - (void)viewDidDisappear:(BOOL)animated {
  190. NSLog(@"Appear");
  191. }
  192. // 移除通知
  193. - (void)dealloc {
  194. NSLog(@"销毁");
  195. [[NSNotificationCenter defaultCenter] removeObserver:self];
  196. }
  197. #pragma mark - 创建聊天室UI(消息列表、输入工具条、提示窗...)
  198. - (void)createUI {
  199. self.view.backgroundColor = [UIColor whiteColor];
  200. CGRect StatusRect = [[UIApplication sharedApplication] statusBarFrame];
  201. CGRect NavRect = self.navigationController.navigationBar.frame;
  202. _navHeight = (RQ_APPLICATION_STATUS_BAR_HEIGHT + RQ_APPLICATION_NAV_BAR_HEIGHT);
  203. // 坐席信息提示
  204. _titleView = [[QMChatTileView alloc] initWithFrame: CGRectMake(0, 0, 150, 40)];
  205. _titleView.nameLabel.text = NSLocalizedString(@"title.people", nil);
  206. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.connection", nil);
  207. _titleView.intrinsicContentSize = CGSizeMake(150, 40);
  208. self.navigationItem.titleView = _titleView;
  209. // 消息列表
  210. self.chatTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, RQ_SCREEN_WIDTH, RQ_SCREEN_HEIGHT-kInputViewHeight-_navHeight- RQ_APPLICATION_SAFEAREA_BOTTOM_HEIGHT) style:UITableViewStylePlain];
  211. self.chatTableView.delegate = self;
  212. self.chatTableView.dataSource = self;
  213. self.chatTableView.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1];
  214. self.chatTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  215. self.chatTableView.estimatedRowHeight = 0;
  216. [self.view addSubview:self.chatTableView];
  217. __weak QMChatRoomViewController * myChatView = self;
  218. MJRefreshNormalHeader *mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
  219. [myChatView Refresh];
  220. }];
  221. [mj_header.lastUpdatedTimeLabel setHidden:true];
  222. self.chatTableView.mj_header = mj_header;
  223. // 输入工具条
  224. self.chatInputView = [[QMChatRoomInputView alloc] initWithFrame:CGRectMake(0, RQ_SCREEN_HEIGHT-kInputViewHeight-_navHeight- RQ_APPLICATION_SAFEAREA_BOTTOM_HEIGHT, RQ_SCREEN_WIDTH, kInputViewHeight)];
  225. [self.chatInputView.voiceButton addTarget:self action:@selector(voiceBtnAction:) forControlEvents:UIControlEventTouchUpInside];
  226. [self.chatInputView.faceButton addTarget:self action:@selector(faceBtnAction:) forControlEvents:UIControlEventTouchUpInside];
  227. [self.chatInputView.addButton addTarget:self action:@selector(addBtnAction:) forControlEvents:UIControlEventTouchUpInside];
  228. [self.chatInputView.RecordBtn addTarget:self action:@selector(cancelRecord:) forControlEvents:UIControlEventTouchUpOutside];
  229. [self.chatInputView.RecordBtn addTarget:self action:@selector(RecordBtnBegin:) forControlEvents:UIControlEventTouchDown];
  230. [self.chatInputView.RecordBtn addTarget:self action:@selector(RecordBtnEnd:) forControlEvents:UIControlEventTouchUpInside];
  231. [self.chatInputView.RecordBtn addTarget:self action:@selector(RecordBtnExit:) forControlEvents:UIControlEventTouchDragExit];
  232. [self.chatInputView.RecordBtn addTarget:self action:@selector(RecordBtnEnter:) forControlEvents:UIControlEventTouchDragEnter];
  233. self.chatInputView.inputView.delegate = self;
  234. [self.view addSubview:self.chatInputView];
  235. // 表情面板
  236. self.faceView = [[TCMessageManagerFaceView alloc] initWithFrame:CGRectMake(0, [UIScreen mainScreen].bounds.size.height, RQ_SCREEN_WIDTH, 216 + RQ_APPLICATION_SAFEAREA_BOTTOM_HEIGHT)];
  237. self.faceView.delegate = self;
  238. [self.faceView.sendButton addTarget:self action:@selector(sendBtnAction:) forControlEvents:UIControlEventTouchUpInside];
  239. // 扩展面板
  240. self.addView = [[QMChatRoomMoreView alloc] initWithFrame:CGRectMake(0, [UIScreen mainScreen].bounds.size.height, RQ_SCREEN_WIDTH, 110 + RQ_APPLICATION_SAFEAREA_BOTTOM_HEIGHT)];
  241. [self.addView.takePicBtn addTarget:self action:@selector(takePicBtnAction) forControlEvents:UIControlEventTouchUpInside];
  242. [self.addView.evaluateBtn addTarget:self action:@selector(evaluateBtnAction) forControlEvents:UIControlEventTouchUpInside];
  243. [self.addView.takeFileBtn addTarget:self action:@selector(takeFileBtnAction) forControlEvents:UIControlEventTouchUpInside];
  244. // 转人工
  245. self.manualButotn = [UIButton buttonWithType:UIButtonTypeSystem];
  246. self.manualButotn.frame = CGRectMake(0, 0, 60, 30);
  247. self.manualButotn.titleLabel.font = [UIFont systemFontOfSize:16];
  248. [self.manualButotn setTitle: NSLocalizedString(@"button.topeople", nil) forState:UIControlStateNormal];
  249. [self.manualButotn setTitleColor:[UIColor colorWithRed:13/255.0 green:139/255.0 blue:249/255.0 alpha:1] forState:UIControlStateNormal];
  250. [self.manualButotn addTarget:self action:@selector(customClick) forControlEvents:UIControlEventTouchUpInside];
  251. if ([QMConnect allowRobot]) {
  252. self.isRobot = true;
  253. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:self.manualButotn];
  254. }else {
  255. self.isRobot = false;
  256. }
  257. // 注销
  258. self.logoutButton = [UIButton buttonWithType:UIButtonTypeSystem];
  259. self.logoutButton.frame = CGRectMake(0, 0, 50, 30);
  260. self.logoutButton.titleLabel.font = [UIFont systemFontOfSize:16];
  261. [self.logoutButton setTitle:NSLocalizedString(@"button.logout", nil) forState:UIControlStateNormal];
  262. [self.logoutButton setTitleColor:[UIColor colorWithRed:13/255.0 green:139/255.0 blue:249/255.0 alpha:1] forState:UIControlStateNormal];
  263. [self.logoutButton addTarget:self action:@selector(logoutAction) forControlEvents:UIControlEventTouchUpInside];
  264. self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:self.logoutButton];
  265. self.indicatorView = [[QMRecordIndicatorView alloc] init];
  266. self.indicatorView.frame = CGRectMake((RQ_SCREEN_WIDTH-150)/2, (RQ_SCREEN_HEIGHT-150-_navHeight-50- RQ_APPLICATION_SAFEAREA_BOTTOM_HEIGHT)/2, 150, 150);
  267. UITapGestureRecognizer * gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideKeyboard)];
  268. [self.chatTableView addGestureRecognizer:gestureRecognizer];
  269. gestureRecognizer.cancelsTouchesInView = NO;
  270. }
  271. - (void)hideKeyboard {
  272. [self.chatInputView.inputView resignFirstResponder];
  273. self.chatInputView.inputView.inputView = nil;
  274. }
  275. #pragma mark - 获取数据(数据模型已存储本地)
  276. // 获取消息数据
  277. - (void)getData {
  278. _dataArray = [NSMutableArray arrayWithArray:[QMConnect getDataFromDatabase:_dataNum]];
  279. /**
  280. 获取同一个accessid(AppKey)下的全部信息 用下面此接口
  281. _dataArray = [NSMutableArray arrayWithArray:[QMConnect getAccessidAllDataFormDatabase:_dataNum]];
  282. */
  283. /**
  284. 获取同一个userId下的全部信息 用下面此接口
  285. _dataArray = [NSMutableArray arrayWithArray:[QMConnect getUserIdDataFormDatabase:_dataNum]];
  286. */
  287. }
  288. // 获取后台配置信息 、 满意度调查 、回复超时时间
  289. - (void)getInvestigateData {
  290. [QMConnect sdkGetInvestigate:^(NSArray * _Nonnull investigateArray) {
  291. dispatch_async(dispatch_get_main_queue(), ^{
  292. _investigateArray = investigateArray;
  293. });
  294. } failureBlock:^(NSString *str){
  295. dispatch_async(dispatch_get_main_queue(), ^{
  296. _investigateArray = [NSArray array];
  297. });
  298. }];
  299. }
  300. // 刷新TableView
  301. -(void)reloadTableView {
  302. if (_titleViewText != nil) {
  303. _titleView.stateInfoLabel.text = _titleViewText;
  304. }
  305. NSLog(@"刷新信息");
  306. [self.chatTableView reloadData];
  307. dispatch_async(dispatch_get_main_queue(), ^{
  308. [self scrollToEnd];
  309. });
  310. }
  311. /**
  312. 新消息通知、发送和接收的消息 都会走这个通知 统一刷新聊天界面
  313. */
  314. - (void)getNewReload: (NSNotification *)sender {
  315. NSLog(@"获取到新消息 %@", sender.object);
  316. [self getData];
  317. [self reloadTableView];
  318. if (backStatus.isValid) {
  319. [backStatus invalidate];
  320. }
  321. }
  322. // 滑动到底部
  323. - (void)scrollToEnd {
  324. if (_dataArray.count>0) {
  325. [_chatTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:_dataArray.count-1 inSection:0] atScrollPosition:UITableViewScrollPositionNone animated:NO];
  326. }
  327. }
  328. // 下拉刷新
  329. - (void)Refresh {
  330. if (_dataNum>_dataArray.count) {
  331. }
  332. _dataNum = _dataNum+10;
  333. [self getData];
  334. [_chatTableView reloadData];
  335. [self.chatTableView.mj_header endRefreshing];
  336. }
  337. //商品信息的卡片(默认是关闭的,需要手动打开注释)
  338. - (void)insertCardInfoMessage {
  339. [QMConnect deleteCardTypeMessage];
  340. NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
  341. [dic setObject:@"https://fs-km.7moor.com/N00000000143/km/2018-06-15/1529044921332/35522b40-7067-11e8-ab29-a3347bfc2358" forKey:@"cardImage"]; //此参数需要填写的是URL
  342. [dic setObject:@"标题" forKey:@"cardHeader"];
  343. [dic setObject:@"副标题" forKey:@"cardSubhead"];
  344. [dic setObject:@"价格" forKey:@"cardPrice"];
  345. [dic setObject:@"https://kf.7moor.com" forKey:@"cardUrl"]; //此参数需要填写的是URL
  346. [QMConnect insertCardInfoData:dic type:@""];
  347. [self getData];
  348. [self reloadTableView];
  349. }
  350. - (void)applicationWillResignActive {
  351. NSLog(@"退到后台");
  352. }
  353. - (void)applicationDidBecomeActive {
  354. NSLog(@"返回前台");
  355. }
  356. #pragma mark - TableViewDelegate TableViewDataSource
  357. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  358. return _dataArray.count;
  359. }
  360. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  361. CustomMessage * message = _dataArray[_dataArray.count-indexPath.row-1];
  362. NSString * identifier = @"";
  363. if ([message.messageType isEqualToString:@"text"]) {
  364. if ([message.isRobot isEqualToString:@"1"]) {
  365. identifier = NSStringFromClass([QMChatRoomRobotCell class]);
  366. }else {
  367. identifier = NSStringFromClass([QMChatRoomTextCell class]);
  368. }
  369. }else if ([message.messageType isEqualToString:@"voice"]) {
  370. identifier = NSStringFromClass([QMChatRoomVoiceCell class]);
  371. }else if ([message.messageType isEqualToString:@"image"]) {
  372. identifier = NSStringFromClass([QMChatRoomImageCell class]);
  373. }else if ([message.messageType isEqualToString:@"investigate"]) {
  374. identifier = NSStringFromClass([QMChatRoomInvestigateCell class]);
  375. }else if ([message.messageType isEqualToString:@"file"]) {
  376. if ([message.fileName.pathExtension.lowercaseString isEqual: @"mp3"]) {
  377. identifier = NSStringFromClass([QMChatRoomMp3Cell class]);//MP3文件以语音形式播放
  378. }else{
  379. identifier = NSStringFromClass([QMChatRoomFileCell class]);
  380. }
  381. }else if ([message.messageType isEqualToString:@"iframe"]) {
  382. identifier = NSStringFromClass([QMChatRoomIframeCell class]);
  383. }else if ([message.messageType isEqualToString:@"richText"]){
  384. identifier = NSStringFromClass([QMChatRoomRichTextCell class]);
  385. }else if ([message.messageType isEqualToString:@"withdrawMessage"]) {
  386. identifier = NSStringFromClass([QMChatRoomNoteCell class]);
  387. }else if ([message.messageType isEqualToString:@"card"]){
  388. identifier = NSStringFromClass([QMChatRoomCardCell class]);
  389. }else if ([message.messageType isEqualToString:@"cardInfo"]) {
  390. identifier = NSStringFromClass([QMChatRoomRichTextCell class]);
  391. }else {
  392. identifier = NSStringFromClass([QMChatRoomTextCell class]);
  393. }
  394. QMChatRoomBaseCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
  395. if (cell == nil) {
  396. cell = [QMChatRoomCellFactory createCellWithClassName:identifier cellModel:message indexPath:indexPath];
  397. }
  398. if (indexPath.row>0) {
  399. CustomMessage * preMessage = _dataArray[_dataArray.count-indexPath.row];
  400. UInt64 disTime = message.createdTime.longLongValue - preMessage.createdTime.longLongValue;
  401. if (disTime<3*60*1000) {
  402. cell.timeLabel.hidden = YES;
  403. }else {
  404. cell.timeLabel.hidden = NO;
  405. }
  406. }else {
  407. cell.timeLabel.hidden = NO;
  408. }
  409. cell.selectionStyle = UITableViewCellSelectionStyleNone;
  410. // 如果需要设置头像、该方法加上一个头像路径参数
  411. [cell setData:message avater:self.avaterStr];
  412. if ([message.messageType isEqualToString:@"file"]) {
  413. [cell setProgress: self.upProgress];
  414. }
  415. if ([message.messageType isEqualToString:@"text"]) {
  416. cell.tapNetAddress = ^(NSString *address) {
  417. if ([address rangeOfString:@"http://"].location == NSNotFound && [address rangeOfString:@"https://"].location == NSNotFound) {
  418. address = [NSString stringWithFormat:@"http://%@", address];
  419. }
  420. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:address]];
  421. };
  422. }
  423. // 机器人消息可以选择问题代码 答案可以选择有帮助或无帮助
  424. if ([message.messageType isEqualToString:@"text"] && [message.isRobot isEqualToString:@"1"]) {
  425. __weak QMChatRoomViewController *weakSelf = self;
  426. cell.tapSendMessage = ^(NSString *text) {
  427. [weakSelf sendText:text];
  428. };
  429. cell.didBtnAction = ^(BOOL isUseful) {
  430. if (!message.isUseful||[message.isUseful isEqualToString:@"none"]) {
  431. if ([weakSelf.heightCaches objectForKey:message._id]) {
  432. [weakSelf.heightCaches removeObjectForKey:message._id];
  433. }
  434. [weakSelf sendRobotFeedback:isUseful questionId:message.questionId messageId:message._id robotType:message.robotType robotId:message.robotId robotMsgId:message.robotMsgId];
  435. }
  436. };
  437. }
  438. return cell;
  439. }
  440. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
  441. CustomMessage * message = _dataArray[_dataArray.count-indexPath.row-1];
  442. if ([message.messageType isEqualToString:@"withdrawMessage"]) {
  443. }else if ([message.messageType isEqualToString:@"cardInfo"]) {
  444. }else{
  445. // 取已有cell高度 时间高度计算
  446. if ([self.heightCaches objectForKey:message._id] && indexPath.row != 0) {
  447. NSNumber *tmpHeight = [self.heightCaches objectForKey:message._id];
  448. return tmpHeight.doubleValue;
  449. }
  450. }
  451. CGFloat height = 15;
  452. if (indexPath.row>0) {
  453. CustomMessage * preMessage = _dataArray[_dataArray.count-indexPath.row];
  454. UInt64 disTime = message.createdTime.longLongValue - preMessage.createdTime.longLongValue;
  455. if (disTime<3*60*1000) {
  456. height = 15;
  457. }else {
  458. height = 45;
  459. }
  460. }else {
  461. height = 45;
  462. }
  463. if ([message.messageType isEqualToString:@"text"]) {
  464. if ([message.isRobot isEqualToString:@"1"]) {
  465. CGFloat robotHeight = [QMTextModel calcRobotHeight:message.message];
  466. height += (robotHeight+5+21);
  467. }else {
  468. CGFloat textHeight = [QMTextModel calcTextHeight:message.message];
  469. height += (textHeight+5+21);
  470. }
  471. height = height>65?height:65;
  472. if ([message.isRobot isEqualToString:@"1"] && ![message.questionId isEqualToString:@""]) {
  473. if (message.isUseful) {
  474. if ([message.isUseful isEqualToString:@"none"]) {
  475. height += 30;
  476. }else {
  477. height += 60;
  478. }
  479. }else {
  480. height += 30;
  481. }
  482. }
  483. }else if ([message.messageType isEqualToString:@"voice"]) {
  484. height += 45;
  485. }else if ([message.messageType isEqualToString:@"image"]) {
  486. height += 140;
  487. }else if ([message.messageType isEqualToString:@"investigate"]) {
  488. height += 145;
  489. }else if ([message.messageType isEqualToString:@"file"]) {
  490. height += 50;
  491. }else if ([message.messageType isEqualToString:@"iframe"]) {
  492. height += message.height.intValue+30;
  493. }else if ([message.messageType isEqualToString:@"richText"]) {
  494. height += 120;
  495. }else if ([message.messageType isEqualToString:@"withdrawMessage"]) {
  496. height = 45;
  497. }else if ([message.messageType isEqualToString:@"card"]) {
  498. height += 150;
  499. }else if ([message.messageType isEqualToString:@"cardInfo"]) {
  500. height += 80;
  501. }else {
  502. height += 45;
  503. }
  504. // 存储cell高度 如果高度有变化 请提前清除对应id的缓存
  505. NSNumber *number = [NSNumber numberWithDouble:height];
  506. [self.heightCaches setObject:number forKey:message._id];
  507. return height;
  508. }
  509. #pragma mark - InputView Action
  510. //切换录音按钮
  511. - (void)voiceBtnAction:(UIButton *)button {
  512. if (self.chatInputView.RecordBtn.hidden == YES) {
  513. [self.chatInputView showRecordButton:YES];
  514. [self.chatInputView.inputView endEditing:YES];
  515. }else {
  516. [self.chatInputView showRecordButton:NO];
  517. self.chatInputView.inputView.inputView = nil;
  518. [self.chatInputView.inputView becomeFirstResponder];
  519. [self.chatInputView.inputView reloadInputViews];
  520. }
  521. }
  522. //表情按钮
  523. - (void)faceBtnAction:(UIButton *)button {
  524. if (button.tag == 1) {
  525. [self.chatInputView showEmotionView:YES];
  526. self.chatInputView.inputView.inputView = self.faceView;
  527. }else {
  528. [self.chatInputView showEmotionView:NO];
  529. self.chatInputView.inputView.inputView = nil;
  530. }
  531. [self.chatInputView.inputView becomeFirstResponder];
  532. [self.chatInputView.inputView reloadInputViews];
  533. }
  534. //扩展功能按钮
  535. - (void)addBtnAction:(UIButton *)button {
  536. if (button.tag == 3) {
  537. [self.chatInputView showMoreView:YES];
  538. self.chatInputView.inputView.inputView = self.addView;
  539. [self.chatInputView.inputView becomeFirstResponder];
  540. [self.chatInputView.inputView reloadInputViews];
  541. }else {
  542. [self.chatInputView showMoreView:NO];
  543. self.chatInputView.inputView.inputView = nil;
  544. [self.chatInputView.inputView endEditing:YES];
  545. }
  546. }
  547. #pragma mark - Record Action
  548. // 开始录音
  549. - (void)RecordBtnBegin:(UIButton *)button {
  550. NSString *fileName = [[NSUUID new] UUIDString];
  551. // 验证权限
  552. AVAuthorizationStatus authorizationStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
  553. switch (authorizationStatus) {
  554. case AVAuthorizationStatusNotDetermined:
  555. [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
  556. }];
  557. break;
  558. case AVAuthorizationStatusAuthorized:
  559. self.indicatorView.isCount = false;
  560. [self.indicatorView changeViewStatus:QMIndicatorStatusNormal];
  561. [self.view addSubview:self.indicatorView];
  562. [self changeButtonStatus:YES];
  563. [[QMAudioRecorder sharedInstance] startAudioRecord:fileName maxDuration:60.0 delegate:self];
  564. break;
  565. case AVAuthorizationStatusRestricted:
  566. NSLog(@"麦克风访问受限!");
  567. break;
  568. case AVAuthorizationStatusDenied:
  569. NSLog(@"设置允许访问麦克风");
  570. break;
  571. }
  572. }
  573. // 结束录音
  574. - (void)RecordBtnEnd:(UIButton *)button {
  575. [[QMAudioRecorder sharedInstance] stopAudioRecord];
  576. }
  577. // 取消录音
  578. - (void)cancelRecord: (UIButton *)button {
  579. [[QMAudioRecorder sharedInstance] cancelAudioRecord];
  580. [self.indicatorView removeFromSuperview];
  581. [self changeButtonStatus:NO];
  582. }
  583. - (void)RecordBtnExit: (UIButton *)button {
  584. [self.indicatorView changeViewStatus:QMIndicatorStatusCancel];
  585. }
  586. - (void)RecordBtnEnter: (UIButton *)button {
  587. [self.indicatorView changeViewStatus:QMIndicatorStatusNormal];
  588. }
  589. // 更改按钮状态
  590. - (void)changeButtonStatus:(BOOL)down {
  591. if (down == YES) {
  592. [self.chatInputView.RecordBtn setTitle:NSLocalizedString(@"button.recorder_recording", nil) forState:UIControlStateNormal];
  593. [self.chatInputView.RecordBtn setTitleColor:[UIColor colorWithRed:50/255.0f green:167/255.0f blue:255/255.0f alpha:1.0] forState:UIControlStateNormal];
  594. self.chatInputView.RecordBtn.layer.borderColor = [[UIColor colorWithRed:50/255.0f green:167/255.0f blue:255/255.0f alpha:1.0] CGColor];
  595. [self.chatInputView.RecordBtn setTintColor:[UIColor colorWithRed:50/255.0f green:167/255.0f blue:255/255.0f alpha:1.0]];
  596. }else {
  597. [self.chatInputView.RecordBtn setTitle:NSLocalizedString(@"button.recorder_normal", nil) forState:UIControlStateNormal];
  598. [self.chatInputView.RecordBtn setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
  599. self.chatInputView.RecordBtn.layer.borderColor = [[UIColor grayColor] CGColor];
  600. }
  601. }
  602. - (void)audioRecorderStart {
  603. }
  604. - (void)audioRecorderCompletion:(NSString *)fileName duration:(NSString *)duration {
  605. NSString * path = [NSString stringWithFormat:@"%@/%@/%@",NSHomeDirectory(),@"Documents",fileName];
  606. [SJVoiceTransform stransformToMp3ByUrlWithUrl:path];
  607. [self sendAudio:fileName duration:duration];
  608. if (duration.intValue >= 60) {
  609. [self.indicatorView changeViewStatus:QMIndicatorStatusLong];
  610. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  611. [self.indicatorView removeFromSuperview];
  612. [self changeButtonStatus:NO];
  613. });
  614. }else {
  615. [self.indicatorView removeFromSuperview];
  616. [self changeButtonStatus:NO];
  617. }
  618. }
  619. - (void)audioRecorderChangeInTimer:(NSTimeInterval)power total:(int)count {
  620. [self.indicatorView updateImageWithPower:power];
  621. self.indicatorView.count = count;
  622. }
  623. - (void)audioRecorderCancel {
  624. [self.indicatorView removeFromSuperview];
  625. [self changeButtonStatus:NO];
  626. }
  627. - (void)audioRecorderFail {
  628. [self.indicatorView changeViewStatus:QMIndicatorStatusShort];
  629. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  630. [self.indicatorView removeFromSuperview];
  631. [self changeButtonStatus:NO];
  632. });
  633. }
  634. #pragma mark - MoreView Action
  635. //通过摄像头获取图片
  636. - (void)photoBtnAction {
  637. if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
  638. UIImagePickerController * imagePicker = [[UIImagePickerController alloc] init];
  639. imagePicker.delegate = self;
  640. imagePicker.allowsEditing = NO;
  641. imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
  642. imagePicker.modalPresentationStyle = UIModalPresentationFullScreen;
  643. [self presentViewController:imagePicker animated:YES completion:nil];
  644. }
  645. }
  646. //相机代理方法
  647. - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
  648. if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
  649. UIImage * myImage = [info objectForKey:UIImagePickerControllerOriginalImage];
  650. [picker dismissViewControllerAnimated:YES completion:nil];
  651. UIImageWriteToSavedPhotosAlbum(myImage, nil, nil, nil);
  652. [self sendImage:myImage];
  653. }
  654. }
  655. //从相册获取图片
  656. - (void)takePicBtnAction {
  657. TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:5 delegate:nil];
  658. [imagePickerVc setDidFinishPickingPhotosHandle:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isStop) {
  659. for (UIImage *image in photos) {
  660. [self sendImage:image];
  661. }
  662. }];
  663. imagePickerVc.modalPresentationStyle = UIModalPresentationFullScreen;
  664. [self presentViewController:imagePickerVc animated:YES completion:nil];
  665. }
  666. // 获取文件
  667. - (void)takeFileBtnAction {
  668. QMFileManagerController * fileViewController = [[QMFileManagerController alloc] init];
  669. [self.navigationController pushViewController:fileViewController animated:true];
  670. }
  671. // 满意度评价
  672. - (void)evaluateBtnAction {
  673. UIAlertController * investigateAlertView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"button.chat_evaluate", nil) message:nil preferredStyle:UIAlertControllerStyleAlert];
  674. for (NSDictionary * index in _investigateArray) {
  675. UIAlertAction * action = [UIAlertAction actionWithTitle:[index objectForKey:@"name"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  676. [QMConnect sdkSubmitInvestigate:[index objectForKey:@"name"] value:[index objectForKey:@"value"] successBlock:^{
  677. NSLog(@"评价成功");
  678. } failBlock:^(NSString *str) {
  679. NSLog(@"评价失败");
  680. }];
  681. }];
  682. [investigateAlertView addAction:action];
  683. }
  684. UIAlertAction * cancelAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"button.cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
  685. }];
  686. [investigateAlertView addAction:cancelAction];
  687. [self presentViewController:investigateAlertView animated:YES completion:nil];
  688. }
  689. #pragma mark - Send Message
  690. // 发送文本
  691. - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
  692. if ([text isEqual:@"\n"]) {
  693. if (![_chatInputView.inputView.text isEqualToString:@""]) {
  694. NSString *text = [_chatInputView.inputView.textStorage getRichString];
  695. [self sendText:text];
  696. _chatInputView.inputView.text = @"";
  697. return NO;
  698. }
  699. return NO;
  700. }
  701. return YES;
  702. }
  703. //发送表情
  704. - (void)sendBtnAction:(UIButton *)button {
  705. if (![_chatInputView.inputView.text isEqualToString:@""]) {
  706. NSString *text = [_chatInputView.inputView.textStorage getRichString];
  707. [self sendText:text];
  708. _chatInputView.inputView.text = @"";
  709. }
  710. }
  711. - (void)sendText:(NSString *)text {
  712. [QMConnect sendMsgText:text successBlock:^{
  713. NSLog(@"发送成功");
  714. dispatch_async(dispatch_get_main_queue(), ^{
  715. [self createNSTimer];
  716. });
  717. } failBlock:^(NSString *str) {
  718. NSLog(@"发送失败");
  719. }];
  720. }
  721. // 发送图片
  722. - (void)sendImage:(UIImage *)image {
  723. [QMConnect sendMsgPic:image successBlock:^{
  724. NSLog(@"图片发送成功");
  725. dispatch_async(dispatch_get_main_queue(), ^{
  726. [self createNSTimer];
  727. });
  728. } failBlock:^(NSString *str) {
  729. NSLog(@"图片发送失败");
  730. }];
  731. }
  732. // 发送语音
  733. - (void)sendAudio:(NSString *)fileName duration:(NSString *)duration {
  734. NSString *filePath = [NSString stringWithFormat:@"%@.mp3", fileName];
  735. [QMConnect sendMsgAudio:filePath duration:duration successBlock:^{
  736. NSLog(@"语音发送成功");
  737. dispatch_async(dispatch_get_main_queue(), ^{
  738. [self createNSTimer];
  739. });
  740. } failBlock:^(NSString *str) {
  741. NSLog(@"语音发送失败");
  742. }];
  743. }
  744. // 发送文件消息
  745. - (void)sendFileMessageWithName:(NSString *)fileName AndSize:(NSString *)fileSize AndPath:(NSString *)filePath {
  746. [QMConnect sendMsgFile:fileName filePath:filePath fileSize:fileSize progressHander:^(float progress) {
  747. dispatch_async(dispatch_get_main_queue(), ^{
  748. self.upProgress = progress;
  749. [self.chatTableView reloadData];
  750. });
  751. } successBlock:^{
  752. NSLog(@"文件上传成功");
  753. dispatch_async(dispatch_get_main_queue(), ^{
  754. [self createNSTimer];
  755. self.upProgress = 0;
  756. });
  757. } failBlock:^(NSString *str) {
  758. NSLog(@"文件上传失败");
  759. dispatch_async(dispatch_get_main_queue(), ^{
  760. self.upProgress = 0;
  761. });
  762. }];
  763. }
  764. // 失败消息重新发送
  765. - (void)resendAction:(QMTapGestureRecognizer *)gestureRecognizer {
  766. NSArray * dataArray = [[NSArray alloc] init];
  767. dataArray = [QMConnect getOneDataFromDatabase:gestureRecognizer.messageId];
  768. for (CustomMessage * custom in dataArray) {
  769. [QMConnect resendMessage:custom successBlock:^{
  770. NSLog(@"重新发送成功");
  771. dispatch_async(dispatch_get_main_queue(), ^{
  772. [self createNSTimer];
  773. });
  774. } failBlock:^(NSString *str) {
  775. NSLog(@"重新发送失败");
  776. }];
  777. }
  778. }
  779. // 表情代理及相关处理
  780. - (void)SendTheFaceStr:(NSString *)faceStr isDelete:(BOOL)dele {
  781. if (dele) {
  782. [_chatInputView.inputView deleteBackward];
  783. }else {
  784. [self insertEmoji:faceStr];
  785. }
  786. }
  787. - (void)insertEmoji: (NSString *)code {
  788. QMTextAttachment * emojiTextAttemt = [QMTextAttachment new];
  789. NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"QMEmoticon" ofType:@"bundle"];
  790. NSString *fileName = [[NSBundle mainBundle] pathForResource:@"expressionImage" ofType:@"plist"];
  791. NSDictionary *plistDict = [NSDictionary dictionaryWithContentsOfFile:fileName];
  792. if ([plistDict objectForKey:code] != nil) {
  793. emojiTextAttemt.emojiCode = code;
  794. emojiTextAttemt.image = [UIImage imageNamed:[NSString stringWithFormat:@"%@/%@", bundlePath, [plistDict objectForKey:code]]];
  795. emojiTextAttemt.bounds = CGRectMake(0, 0, 18, 18);
  796. NSAttributedString * attributeString = [NSAttributedString attributedStringWithAttachment:emojiTextAttemt];
  797. NSRange range = [_chatInputView.inputView selectedRange];
  798. if (range.length > 0) {
  799. [_chatInputView.inputView.textStorage deleteCharactersInRange:range];
  800. }
  801. [_chatInputView.inputView.textStorage insertAttributedString:attributeString atIndex:[_chatInputView.inputView selectedRange].location];
  802. _chatInputView.inputView.selectedRange = NSMakeRange(_chatInputView.inputView.selectedRange.location+1, 0);
  803. }
  804. [self resetTextStyle];
  805. }
  806. - (void)resetTextStyle {
  807. NSRange wholeRange = NSMakeRange(0, _chatInputView.inputView.textStorage.length);
  808. [_chatInputView.inputView.textStorage removeAttribute:NSFontAttributeName range:wholeRange];
  809. [_chatInputView.inputView.textStorage addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:18] range:wholeRange];
  810. _chatInputView.inputView.font = [UIFont systemFontOfSize:18];
  811. }
  812. // 机器人帮助评价
  813. - (void)sendRobotFeedback: (BOOL)isUseful questionId: (NSString *)questionId messageId: (NSString *)messageId robotType: (NSString *)robotType robotId: (NSString *)robotId robotMsgId: (NSString *)robotMsgId {
  814. __weak QMChatRoomViewController *weakSelf = self;
  815. [QMConnect sdkSubmitRobotFeedback:isUseful questionId:questionId messageId:messageId robotType:robotType robotId:robotId robotMsgId:robotMsgId successBlock:^{
  816. dispatch_async(dispatch_get_main_queue(), ^{
  817. [weakSelf getData];
  818. [weakSelf.chatTableView reloadData];
  819. });
  820. } failBlock:^(NSString *str) {
  821. }];
  822. }
  823. #pragma mark - Multi Function
  824. // 注销事件
  825. - (void)logoutAction {
  826. [[QMAudioPlayer sharedInstance] stopAudioPlayer];
  827. [QMConnect logout];
  828. [self removeTimer];
  829. NSArray *viewcontrollers = self.navigationController.viewControllers;
  830. if (viewcontrollers.count>1) {
  831. if ([viewcontrollers objectAtIndex:viewcontrollers.count-1]==self) {
  832. //push方式
  833. [self.navigationController popViewControllerAnimated:YES];
  834. }
  835. }
  836. else{
  837. //present方式
  838. [self.navigationController dismissViewControllerAnimated:YES completion:^{
  839. [self.navigationController dismissViewControllerAnimated:YES completion:nil];
  840. }];
  841. }
  842. }
  843. // 转人工客服
  844. - (void)customClick {
  845. // [QMConnect sdkConvertManual:^{
  846. // NSLog(@"转人工客服成功");
  847. //// [self insertCardInfoMessage];
  848. // } failBlock:^(NSString *str) {
  849. // NSLog(@"转人工客服失败");
  850. // dispatch_async(dispatch_get_main_queue(), ^{
  851. // if (self.isOpenSchedule == false) {
  852. // [self showGuestBookViewController];
  853. // }
  854. // });
  855. // }];
  856. [QMConnect sdkConvertManual:@"" successBlock:^{
  857. NSLog(@"转人工客服成功");
  858. } failBlock:^(NSString *str) {
  859. NSLog(@"转人工客服失败");
  860. dispatch_async(dispatch_get_main_queue(), ^{
  861. if (self.isOpenSchedule == false) {
  862. [self showGuestBookViewController];
  863. }
  864. });
  865. }];
  866. }
  867. #pragma mark - 留言提示
  868. - (void)showGuestBookViewController {
  869. [self.chatInputView setHidden:true];
  870. [self.manualButotn setHidden:true];
  871. self.chatTableView.frame = CGRectMake(0, 0, RQ_SCREEN_WIDTH, RQ_SCREEN_HEIGHT - _navHeight- RQ_APPLICATION_SAFEAREA_BOTTOM_HEIGHT);
  872. self.msg = [QMConnect leaveMessageAlert];
  873. if ([self.msg isEqualToString:@""]) {
  874. self.msg = NSLocalizedString(@"title.messageprompts", nil);
  875. }
  876. if ([QMConnect allowedLeaveMessage]) {
  877. UIAlertController *alertView = [UIAlertController alertControllerWithTitle:@"" message: self.msg preferredStyle:UIAlertControllerStyleAlert];
  878. UIAlertAction *sureAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"button.leaveMessage", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  879. QMChatRoomGuestBookViewController *guestBookViewController = [[QMChatRoomGuestBookViewController alloc] init];
  880. guestBookViewController.peerId = self.peerId;
  881. [self.navigationController pushViewController:guestBookViewController animated:YES];
  882. }];
  883. UIAlertAction *cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"button.signOut", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
  884. [self logoutAction];
  885. }];
  886. [alertView addAction:sureAction];
  887. [alertView addAction:cancel];
  888. [self presentViewController:alertView animated:YES completion:nil];
  889. }else {
  890. UIAlertController *alertView = [UIAlertController alertControllerWithTitle:@"" message: self.msg preferredStyle:UIAlertControllerStyleAlert];
  891. UIAlertAction *cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"title.iknow", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
  892. }];
  893. [alertView addAction:cancel];
  894. [self presentViewController:alertView animated:YES completion:nil];
  895. }
  896. }
  897. #pragma mark - 客服代理方法
  898. /// 当前客服状态
  899. - (void)currentAgentStatus:(QMKStatus)status{
  900. switch (status) {
  901. case 0:
  902. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.now_robit", nil);
  903. self.manualButotn.hidden = NO;
  904. break;
  905. case 1:
  906. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.people_now", nil);
  907. self.manualButotn.hidden = YES;
  908. break;
  909. case 2:
  910. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.people_isline", nil);
  911. self.manualButotn.hidden = NO;
  912. break;
  913. case 3:
  914. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.people_now", nil);
  915. self.manualButotn.hidden = YES;
  916. break;
  917. case 4:
  918. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.people_isleave", nil);
  919. self.manualButotn.hidden = YES;
  920. break;
  921. default:
  922. break;
  923. }
  924. }
  925. /// 当前坐席信息
  926. - (void)currentAgentInfoWithAgent:(QMAgent * _Nonnull)agent {
  927. NSString *string = [NSString stringWithFormat:@"%@(%@)", agent.name, agent.exten];
  928. string = [string stringByReplacingOccurrencesOfString:@"\n" withString:@""];
  929. _titleView.nameLabel.text = [NSString stringWithFormat:@"%@",string];
  930. }
  931. /// 当前会话排队数
  932. - (void)currentSessionWaitNumberWithNumber:(NSInteger)number {
  933. _titleView.stateInfoLabel.text = [NSString stringWithFormat:@"%@: %ld",NSLocalizedString(@"title.line_up", nil), (long)number];
  934. self.manualButotn.hidden = YES;
  935. }
  936. /// 邀请评价
  937. - (void)inviteEvaluate {
  938. UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"" message:NSLocalizedString(@"button.chat_evaluate", nil) preferredStyle:UIAlertControllerStyleActionSheet];
  939. for (NSDictionary *index in _investigateArray) {
  940. [alertController addAction:[UIAlertAction actionWithTitle:[index objectForKey:@"name"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  941. [QMConnect sdkSubmitInvestigate:[index objectForKey:@"name"] value:[index objectForKey:@"value"] successBlock:^{
  942. NSLog(@"评价成功");
  943. } failBlock:^(NSString *str) {
  944. NSLog(@"评价失败");
  945. }];
  946. }]];
  947. };
  948. [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"button.cancel", nil) style:UIAlertActionStyleDestructive handler:nil]];
  949. [self presentViewController:alertController animated:true completion:nil];
  950. }
  951. #pragma mark - Push Notification
  952. // 键盘通知
  953. - (void)keyboardFrameChange: (NSNotification *)notification {
  954. NSDictionary * userInfo = notification.userInfo;
  955. NSValue * value = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
  956. CGRect newFrame = [value CGRectValue];
  957. if (newFrame.origin.y == [UIScreen mainScreen].bounds.size.height) {
  958. [UIView animateWithDuration:0.3 animations:^{
  959. self.chatInputView.frame = CGRectMake(0, RQ_SCREEN_HEIGHT-kInputViewHeight-_navHeight- RQ_APPLICATION_SAFEAREA_BOTTOM_HEIGHT, RQ_SCREEN_WIDTH, kInputViewHeight);
  960. self.chatTableView.frame = CGRectMake(0, 0, RQ_SCREEN_WIDTH, RQ_SCREEN_HEIGHT-_navHeight- RQ_APPLICATION_SAFEAREA_BOTTOM_HEIGHT-kInputViewHeight);
  961. }];
  962. }else {
  963. [UIView animateWithDuration:0.3 animations:^{
  964. self.chatInputView.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height-kInputViewHeight-newFrame.size.height-_navHeight, RQ_SCREEN_WIDTH, kInputViewHeight);
  965. self.chatTableView.frame = CGRectMake(0, 0, RQ_SCREEN_WIDTH, [UIScreen mainScreen].bounds.size.height-_navHeight-kInputViewHeight-newFrame.size.height);
  966. [self scrollToEnd];
  967. }];
  968. }
  969. }
  970. // 机器人客服
  971. - (void)robotAction {
  972. NSLog(@"机器人客服");
  973. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.now_robit", nil);
  974. self.manualButotn.hidden = NO;
  975. self.isRobot = YES;
  976. }
  977. // 客服在线
  978. - (void)customOnline {
  979. NSLog(@"客服在线");
  980. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.people_now", nil);
  981. self.manualButotn.hidden = YES;
  982. self.isRobot = NO;
  983. [self createNSTimer];
  984. }
  985. // 客服离线
  986. - (void)customOffline {
  987. NSLog(@"客服离线");
  988. self.manualButotn.hidden = NO;
  989. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.people_isline", nil);
  990. [self showGuestBookViewController];
  991. }
  992. // 会话领取
  993. - (void)customClaim {
  994. NSLog(@"会话被坐席领取");
  995. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.people_now", nil);
  996. self.manualButotn.hidden = YES;
  997. self.isRobot = NO;
  998. }
  999. // 离线推送 (坐席在后台结束会话,返回上一界面)
  1000. - (void)customFinish {
  1001. NSLog(@"客服结束会话");
  1002. [self.chatInputView setHidden:true];
  1003. self.manualButotn.hidden = YES;
  1004. self.chatTableView.frame = CGRectMake(0, 0, RQ_SCREEN_WIDTH, RQ_SCREEN_HEIGHT - _navHeight- RQ_APPLICATION_SAFEAREA_BOTTOM_HEIGHT);
  1005. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.people_now", nil);
  1006. }
  1007. // 排队人数
  1008. - (void)customQueue: (NSNotification *)notification {
  1009. NSLog(@"排队人数 %@", notification.object);
  1010. _titleView.stateInfoLabel.text = [NSString stringWithFormat:@"%@: %@",NSLocalizedString(@"title.line_up", nil), notification.object];
  1011. self.manualButotn.hidden = YES;
  1012. }
  1013. // 满意度推送
  1014. - (void)customInvestigate {
  1015. NSLog(@"满意度通知");
  1016. UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"" message:NSLocalizedString(@"button.chat_evaluate", nil) preferredStyle:UIAlertControllerStyleActionSheet];
  1017. for (NSDictionary *index in _investigateArray) {
  1018. [alertController addAction:[UIAlertAction actionWithTitle:[index objectForKey:@"name"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  1019. [QMConnect sdkSubmitInvestigate:[index objectForKey:@"name"] value:[index objectForKey:@"value"] successBlock:^{
  1020. NSLog(@"评价成功");
  1021. } failBlock:^(NSString *str) {
  1022. NSLog(@"评价失败");
  1023. }];
  1024. }]];
  1025. };
  1026. [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"button.cancel", nil) style:UIAlertActionStyleDestructive handler:nil]];
  1027. [self presentViewController:alertController animated:true completion:nil];
  1028. }
  1029. // 坐席信息 (坐席工号、坐席名称、坐席头像) 可能为空字符串需要判断
  1030. - (void)customAgentMessage: (NSNotification *)notification {
  1031. QMAgent *agent = notification.object;
  1032. NSString *string;
  1033. if ([agent.type isEqualToString:@"robot"]) {
  1034. string = [NSString stringWithFormat:@"%@", agent.name];
  1035. }else if ([agent.type isEqualToString:@"activeClaim"]){
  1036. string = [NSString stringWithFormat:@"%@(%@)", agent.name, agent.exten];
  1037. [self customOnline];
  1038. }else {
  1039. string = [NSString stringWithFormat:@"%@(%@)", agent.name, agent.exten];
  1040. }
  1041. string = [string stringByReplacingOccurrencesOfString:@"\n" withString:@""];
  1042. _titleView.nameLabel.text = [NSString stringWithFormat:@"%@",string];
  1043. }
  1044. // 专属坐席不在线通知 调用接受其他坐席服务接口成功后调用 beginSession
  1045. - (void)customVIP {
  1046. [QMAlert showMessage:NSLocalizedString(@"title.schedule_notonline", nil)];
  1047. __weak QMChatRoomViewController * myChatView = self;
  1048. [QMConnect sdkAcceptOtherAgentWithPeer:self.peerId successBlock:^{
  1049. NSLog(@"成功");
  1050. [myChatView beginSession];
  1051. } failBlock:^(NSString *str) {
  1052. NSLog(@"失败");
  1053. [QMAlert showMessage:NSLocalizedString(@"title.schedule_faile", nil)];
  1054. }];
  1055. }
  1056. #pragma mark - 日程管理的留言
  1057. - (void)customLeavemsg: (NSNotification*)notification {
  1058. NSArray *array = notification.object;
  1059. NSString *str = array[0];
  1060. [QMConnect sdkGetWebchatScheduleConfig:^(NSDictionary * _Nonnull scheduleDic) {
  1061. dispatch_async(dispatch_get_main_queue(), ^{
  1062. for (NSDictionary*dic in scheduleDic[@"leavemsgNodes"]) {
  1063. if ([str isEqualToString:dic[@"_id"]]){
  1064. self.LeaveArray = dic[@"leavemsgFields"];
  1065. QMChatRoomGuestBookViewController *guestBookViewController = [[QMChatRoomGuestBookViewController alloc] init];
  1066. guestBookViewController.peerId = array[1];
  1067. guestBookViewController.contactFields = self.LeaveArray;
  1068. guestBookViewController.leaveMsg = dic[@"contentTip"];
  1069. guestBookViewController.isScheduleLeave = true;
  1070. [self.navigationController pushViewController:guestBookViewController animated:YES];
  1071. }
  1072. }
  1073. });
  1074. } failBlock:^(NSString *str) {
  1075. NSLog(@"日程管理进入留言失败");
  1076. }];
  1077. }
  1078. - (void)beginSession {
  1079. __weak QMChatRoomViewController * myChatView = self;
  1080. [QMConnect sdkBeginNewChatSession:self.peerId successBlock:^(BOOL remark, NSString *str) {
  1081. NSLog(@"开始会话成功");
  1082. dispatch_async(dispatch_get_main_queue(), ^{
  1083. // 是否启动了评价功能
  1084. if (remark == NO) {
  1085. myChatView.addView.evaluateBtn.hidden = YES;
  1086. myChatView.addView.evaluateLabel.hidden = YES;
  1087. }else {
  1088. myChatView.addView.evaluateBtn.hidden = NO;
  1089. myChatView.addView.evaluateLabel.hidden = NO;
  1090. }
  1091. });
  1092. } failBlock:^(NSString *str) {
  1093. NSLog(@"开始会话失败, %@" , str);
  1094. }];
  1095. }
  1096. // 坐席正在输入
  1097. - (void)changeCustomStatus {
  1098. if (![_titleView.stateInfoLabel.text isEqual: NSLocalizedString(@"title.other_writing", nil)]) {
  1099. NSString *str = _titleView.stateInfoLabel.text;
  1100. _titleViewText = str;
  1101. }
  1102. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.other_writing", nil);
  1103. backStatus = nil;
  1104. backStatus = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(backCustomStatus:) userInfo:nil repeats:NO];
  1105. [[NSRunLoop mainRunLoop] addTimer:backStatus forMode:NSRunLoopCommonModes];
  1106. }
  1107. - (void)backCustomStatus:(NSTimer *)time{
  1108. _titleView.stateInfoLabel.text = _titleViewText;
  1109. [backStatus invalidate];
  1110. }
  1111. // 撤回消息
  1112. - (void)cbangeDrowMessageStatus: (NSNotification*)notification {
  1113. NSString *messageId = notification.object;
  1114. [QMConnect changeDrawMessageStatus:messageId];
  1115. [self getData];
  1116. [self reloadTableView];
  1117. }
  1118. // 访客无响应断开计时(开启无响应断开的需要把方法中的代码放开)
  1119. - (void)createNSTimer{
  1120. NSLog(@"开启无响应定时器");
  1121. // [self removeTimer];
  1122. // self.breakDuration = [QMConnect breakSessionDuration];
  1123. // self.breakTipsDuration = [QMConnect breakSessionAlertDuration];
  1124. // if (self.breakDuration && self.breakTipsDuration && [QMConnect allowedBreakSession]) {
  1125. // breakTipTimer = [NSTimer scheduledTimerWithTimeInterval:self.breakTipsDuration * 60 target:self selector:@selector(breakTipTimerAction:) userInfo:nil repeats:NO];
  1126. // breakTimer = [NSTimer scheduledTimerWithTimeInterval:self.breakDuration * 60 target:self selector:@selector(breakTimerAction:) userInfo:nil repeats:NO];
  1127. // [[NSRunLoop mainRunLoop] addTimer:breakTimer forMode:NSRunLoopCommonModes];
  1128. // [[NSRunLoop mainRunLoop] addTimer:breakTipTimer forMode:NSRunLoopCommonModes];
  1129. // }
  1130. }
  1131. - (void)removeTimer {
  1132. if (breakTipTimer) {
  1133. [breakTipTimer invalidate];
  1134. breakTipTimer = nil;
  1135. }
  1136. if (breakTimer) {
  1137. [breakTimer invalidate];
  1138. breakTimer = nil;
  1139. }
  1140. }
  1141. - (void)breakTimerAction:(NSTimer *)time{
  1142. [self.chatInputView setHidden:true];
  1143. [self.manualButotn setHidden:true];
  1144. self.chatTableView.frame = CGRectMake(0, 0, RQ_SCREEN_WIDTH, RQ_SCREEN_HEIGHT - _navHeight- RQ_APPLICATION_SAFEAREA_BOTTOM_HEIGHT);
  1145. }
  1146. - (void)breakTipTimerAction:(NSTimer *)timer{
  1147. [QMAlert showMessage:[QMConnect breakSessionAlert]];
  1148. [breakTipTimer invalidate];
  1149. }
  1150. - (void)didReceiveMemoryWarning {
  1151. [super didReceiveMemoryWarning];
  1152. // Dispose of any resources that can be recreated.
  1153. }
  1154. @end