QMChatRoomViewController.m 53 KB

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