// // FeedbackVC.swift // QuickLocation // import UIKit import RxSwift import RxCocoa final class FeedbackVC: BaseViewController { private enum FeedbackCategory: String, CaseIterable { case feature = "功能问题" case suggestion = "优化建议" case other = "其他" } private struct FeedbackDraft { let category: FeedbackCategory let detail: String let images: [UIImage] let contact: String } private var rootView: FeedbackView! private var selectedCategory: FeedbackCategory = .feature private var selectedImages: [UIImage] = [] private var pendingDraft: FeedbackDraft? private var isSubmitting = false override func loadView() { rootView = FeedbackView(frame: UIScreen.main.bounds) view = rootView } override func viewDidLoad() { super.viewDidLoad() bind() } private func bind() { rootView.navView.onBackTapped = { [weak self] in guard self?.isSubmitting == false else { return } self?.navigationController?.popViewController(animated: true) } rootView.categoryButtons.forEach { button in button.addTarget(self, action: #selector(categoryTapped(_:)), for: .touchUpInside) } rootView.detailTextView.rx.text.orEmpty .subscribe(onNext: { [weak self] text in self?.handleDetailChange(text) }) .disposed(by: disposeBag) rootView.contactTextField.rx.controlEvent(.editingDidEndOnExit) .subscribe(onNext: { [weak self] in self?.rootView.contactTextField.resignFirstResponder() }) .disposed(by: disposeBag) rootView.onAddImage = { [weak self] in self?.showAlbum() } rootView.onDeleteImage = { [weak self] index in guard let self, self.selectedImages.indices.contains(index) else { return } self.selectedImages.remove(at: index) self.rootView.updateImages(self.selectedImages) } rootView.saveBtn.rx.tap .subscribe(onNext: { [weak self] in self?.submit() }) .disposed(by: disposeBag) } @objc private func categoryTapped(_ sender: UIButton) { let categories = FeedbackCategory.allCases guard categories.indices.contains(sender.tag) else { return } let category = categories[sender.tag] selectedCategory = category rootView.updateCategory(selectedIndex: sender.tag) } private func handleDetailChange(_ text: String) { let limitedText = text.count > 200 ? String(text.prefix(200)) : text if limitedText != text { rootView.detailTextView.text = limitedText rootView.detailTextView.selectedRange = NSRange(location: limitedText.count, length: 0) } rootView.updateDetail(text: limitedText) } private func showAlbum() { let remainingCount = 3 - selectedImages.count guard remainingCount > 0, !isSubmitting else { return } let picker = PhotoPickerVC(maxCount: remainingCount) picker.onConfirm = { [weak self] images in guard let self else { return } self.selectedImages.append(contentsOf: images.prefix(remainingCount)) self.selectedImages = Array(self.selectedImages.prefix(3)) self.rootView.updateImages(self.selectedImages) } present(picker, animated: true) } private func submit() { guard !isSubmitting else { return } view.endEditing(true) let detail = (rootView.detailTextView.text ?? "") .trimmingCharacters(in: .whitespacesAndNewlines) guard !detail.isEmpty else { DLToast.show(text: "请补充详细问题或意见") return } pendingDraft = FeedbackDraft( category: selectedCategory, detail: detail, images: selectedImages, contact: rootView.contactTextField.text ?? "" ) isSubmitting = true rootView.setSubmitting(true) let successView = FeedbackSuccessView() successView.show(in: view) DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self, weak successView] in guard let self, let successView else { return } successView.dismiss { AppRouter.shared.popOrDismiss() } } } }