// // KBAIMessageChatingVC.m // keyBoard // // Created by Mac on 2026/1/28. // #import "KBAIMessageChatingVC.h" #import "AiVM.h" #import "KBChattedCompanionModel.h" #import "KBHUD.h" #import "KBAIChatMessageCacheManager.h" #import /// 聊天会话被重置的通知 static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidResetNotification"; @interface KBAIMessageChatingVC () @property (nonatomic, strong) AiVM *viewModel; @property (nonatomic, strong) NSMutableArray *chattedList; /// 删除按钮 @property (nonatomic, strong) UIButton *deleteButton; /// 当前长按的 indexPath @property (nonatomic, strong) NSIndexPath *longPressIndexPath; @end @implementation KBAIMessageChatingVC #pragma mark - Lifecycle - (void)viewDidLoad { self.listType = 1; // Chatting [super viewDidLoad]; // 添加长按手势 [self setupLongPressGesture]; // 添加点击手势,用于隐藏删除按钮 UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; tapGesture.cancelsTouchesInView = NO; // 不影响 cell 的点击 [self.tableView addGestureRecognizer:tapGesture]; } #pragma mark - 手势处理 - (void)setupLongPressGesture { UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)]; longPress.minimumPressDuration = 0.5; // 长按0.5秒 [self.tableView addGestureRecognizer:longPress]; } - (void)handleLongPress:(UILongPressGestureRecognizer *)gesture { if (gesture.state == UIGestureRecognizerStateBegan) { CGPoint point = [gesture locationInView:self.tableView]; NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:point]; if (indexPath) { // 在手指位置显示删除按钮 [self showDeleteButtonAtPoint:point]; // 在 showDeleteButtonAtPoint 之后再设置,避免被 hideDeleteButton 清空 self.longPressIndexPath = indexPath; } } } - (void)handleTapGesture:(UITapGestureRecognizer *)gesture { // 点击其他地方隐藏删除按钮 if (self.deleteButton && !self.deleteButton.hidden) { CGPoint pointInButton = [gesture locationInView:self.deleteButton]; // 如果点击的不是删除按钮,则隐藏 if (!CGRectContainsPoint(self.deleteButton.bounds, pointInButton)) { NSLog(@"[KBAIMessageChatingVC] 点击了删除按钮外部,隐藏按钮"); [self hideDeleteButton]; } else { NSLog(@"[KBAIMessageChatingVC] 点击了删除按钮内部,不隐藏"); } } } - (void)showDeleteButtonAtPoint:(CGPoint)point { // 隐藏之前的按钮 [self hideDeleteButton]; // 创建删除按钮 if (!self.deleteButton) { self.deleteButton = [UIButton buttonWithType:UIButtonTypeCustom]; [self.deleteButton setTitle:@"删除此记录" forState:UIControlStateNormal]; [self.deleteButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; self.deleteButton.titleLabel.font = [UIFont systemFontOfSize:14]; self.deleteButton.backgroundColor = [UIColor colorWithRed:244/255.0 green:67/255.0 blue:54/255.0 alpha:1.0]; // #F44336 self.deleteButton.layer.cornerRadius = 6; self.deleteButton.layer.masksToBounds = YES; [self.deleteButton addTarget:self action:@selector(deleteButtonTapped) forControlEvents:UIControlEventTouchUpInside]; // 添加阴影效果 self.deleteButton.layer.shadowColor = [UIColor blackColor].CGColor; self.deleteButton.layer.shadowOffset = CGSizeMake(0, 2); self.deleteButton.layer.shadowOpacity = 0.3; self.deleteButton.layer.shadowRadius = 4; } // 添加到父视图(确保在最上层) [self.view addSubview:self.deleteButton]; // 设置按钮大小和位置 CGSize buttonSize = CGSizeMake(110, 40); // 将 tableView 的坐标转换为父视图坐标 CGPoint pointInView = [self.tableView convertPoint:point toView:self.view]; // 计算按钮位置,确保不超出屏幕 CGFloat buttonX = pointInView.x - buttonSize.width / 2; CGFloat buttonY = pointInView.y - buttonSize.height - 10; // 在手指上方10px // 边界检查 CGFloat margin = 10; if (buttonX < margin) { buttonX = margin; } else if (buttonX + buttonSize.width > self.view.bounds.size.width - margin) { buttonX = self.view.bounds.size.width - buttonSize.width - margin; } if (buttonY < KB_NAV_TOTAL_HEIGHT + margin) { // 如果上方空间不够,显示在手指下方 buttonY = pointInView.y + 10; } [self.deleteButton mas_remakeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.view).offset(buttonX); make.top.equalTo(self.view).offset(buttonY); make.width.mas_equalTo(buttonSize.width); make.height.mas_equalTo(buttonSize.height); }]; self.deleteButton.hidden = NO; // 添加弹出动画 self.deleteButton.transform = CGAffineTransformMakeScale(0.3, 0.3); self.deleteButton.alpha = 0; [UIView animateWithDuration:0.2 delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:0.5 options:UIViewAnimationOptionCurveEaseOut animations:^{ self.deleteButton.transform = CGAffineTransformIdentity; self.deleteButton.alpha = 1.0; } completion:nil]; } - (void)hideDeleteButton { if (self.deleteButton) { [UIView animateWithDuration:0.15 animations:^{ self.deleteButton.alpha = 0; self.deleteButton.transform = CGAffineTransformMakeScale(0.8, 0.8); } completion:^(BOOL finished) { self.deleteButton.hidden = YES; [self.deleteButton removeFromSuperview]; }]; } self.longPressIndexPath = nil; } - (void)deleteButtonTapped { if (!self.longPressIndexPath) { return; } // 保存 indexPath,因为后面会清空 NSIndexPath *indexPath = self.longPressIndexPath; // 获取要删除的数据 if (indexPath.row >= self.chattedList.count) { NSLog(@"[KBAIMessageChatingVC] 错误:索引越界,row=%ld, count=%ld", (long)indexPath.row, (long)self.chattedList.count); [self hideDeleteButton]; return; } KBChattedCompanionModel *model = self.chattedList[indexPath.row]; NSInteger companionId = model.companionId; NSLog(@"[KBAIMessageChatingVC] 开始删除聊天记录:companionId=%ld, name=%@", (long)companionId, model.name); // 隐藏删除按钮 [self hideDeleteButton]; // 显示加载提示 [KBHUD show]; __weak typeof(self) weakSelf = self; // 调用清空聊天会话的 API [self.viewModel resetChatSessionWithCompanionId:companionId completion:^(KBChatSessionResetResponse * _Nullable response, NSError * _Nullable error) { dispatch_async(dispatch_get_main_queue(), ^{ [KBHUD dismiss]; if (error) { NSLog(@"[KBAIMessageChatingVC] 删除失败:%@", error.localizedDescription); [KBHUD showError:@"删除失败,请重试"]; return; } NSLog(@"[KBAIMessageChatingVC] ✅ API 调用成功,开始清理本地数据"); // 1. 删除本地列表数据 if (indexPath.row < weakSelf.chattedList.count) { [weakSelf.chattedList removeObjectAtIndex:indexPath.row]; } if (indexPath.row < weakSelf.dataArray.count) { [weakSelf.dataArray removeObjectAtIndex:indexPath.row]; } // 2. 更新 TableView(带动画) [weakSelf.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft]; // 3. ✅ 清除缓存管理器中的聊天记录(关键!) [[KBAIChatMessageCacheManager shared] clearMessagesForCompanionId:companionId]; NSLog(@"[KBAIMessageChatingVC] ✅ 已清除缓存:companionId=%ld", (long)companionId); // 4. 发送通知,通知其他页面(主页)刷新 [[NSNotificationCenter defaultCenter] postNotificationName:KBChatSessionDidResetNotification object:nil userInfo:@{@"companionId": @(companionId)}]; NSLog(@"[KBAIMessageChatingVC] ✅ 已发送重置通知:companionId=%ld", (long)companionId); // 5. 显示成功提示 [KBHUD showSuccess:@"已删除"]; }); }]; } #pragma mark - 2:数据加载 - (void)loadData { [KBHUD show]; __weak typeof(self) weakSelf = self; [self.viewModel fetchChattedCompanionsWithCompletion:^(NSArray * _Nullable list, NSError * _Nullable error) { dispatch_async(dispatch_get_main_queue(), ^{ [KBHUD dismiss]; if (error) { [KBHUD showError:error.localizedDescription]; return; } [weakSelf.chattedList removeAllObjects]; if (list.count > 0) { [weakSelf.chattedList addObjectsFromArray:list]; } [weakSelf.dataArray removeAllObjects]; // 转换为通用数据格式 for (KBChattedCompanionModel *model in weakSelf.chattedList) { NSMutableDictionary *item = [NSMutableDictionary dictionary]; item[@"avatar"] = model.avatarUrl ?: @""; item[@"name"] = model.name ?: @""; item[@"content"] = model.shortDesc ?: @""; item[@"time"] = model.createdAt ?: @""; item[@"isPinned"] = @NO; item[@"companionId"] = @(model.companionId); [weakSelf.dataArray addObject:item]; } [weakSelf.tableView reloadData]; }); }]; } #pragma mark - Lazy Load - (AiVM *)viewModel { if (!_viewModel) { _viewModel = [[AiVM alloc] init]; } return _viewModel; } - (NSMutableArray *)chattedList { if (!_chattedList) { _chattedList = [NSMutableArray array]; } return _chattedList; } @end