// // ScheduleView.swift // QuickLocation // // Created by 八条 on 2026/6/23. // import UIKit import RxSwift import RxCocoa import TagListView class ScheduleView: UIView { var disposeBag = DisposeBag() private func setupRx() { createBtn.rx.tap.subscribe(onNext: { _ in AppRouter.push(Route.createSchedule) }).disposed(by: disposeBag) } private func setupUI() { addSubview(navBgView) addSubview(navBarView) navBarView.addSubview(navTitleLabel) addSubview(headerView) addSubview(travelRouteView) addSubview(tableView) addSubview(createBtn) navBgView.layoutChain .edges(excludingEdge: .bottom) .height(kNaviHeight) navBarView.layoutChain .edges(excludingEdge: .bottom) .height(kNaviHeight) navTitleLabel.layoutChain .top(kStatusBarHeight + 12) .centerX() headerView.layoutChain .topToBottomOfView(navBarView) .edgesHorzontal() // .height(147) travelRouteView.layoutChain .topToBottomOfView(headerView) .edgesHorzontal() tableView.layoutChain .topToBottomOfView(travelRouteView) .edges(excludingEdge: .top) createBtn.layoutChain .bottom(kSafeBottomMargin + 102) .right(10) .width(60) .heightToWidth(1) } lazy var navBgView: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "Common/navBar_bg_1") iv.contentMode = .scaleAspectFill return iv }() lazy var navBarView: UIView = { let view = UIView() view.backgroundColor = .clear return view }() lazy var navTitleLabel: UILabel = { let label = UILabel() label.text = "行程" label.font = .systemFont(ofSize: 18, weight: .medium) label.textColor = ThemeManager.shared.color.titleAuxColor label.textAlignment = .center return label }() /// 谁看过我 lazy var headerView: UIView = { let view = UIView() view.backgroundColor = .clear let titleLab = UILabel() titleLab.text = "谁看过我" titleLab.font = .systemFont(ofSize: 16, weight: .medium) titleLab.textColor = ThemeManager.shared.color.titleAuxColor view.addSubview(titleLab) titleLab.layoutChain .top(15) let dotView = UIView() dotView.backgroundColor = UIColor(hexStr: "#16B3FF") dotView.cornerRadius = 2 view.addSubview(dotView) dotView.layoutChain .left(15) .centerY(titleLab) .width(4) .height(11) titleLab.layoutChain.leftToRightOfView(dotView, offset: 5) let historyBtn = UIButton() historyBtn.setTitle("历史行程", for: .normal) historyBtn.setTitleColor(ThemeManager.shared.color.titleAuxColor, for: .normal) historyBtn.titleLabel?.font = .systemFont(ofSize: 12, weight: .medium) historyBtn.rx.tap.subscribe(onNext: { _ in AppRouter.push(Route.scheduleHistory) }).disposed(by: disposeBag) view.addSubview(historyBtn) historyBtn.layoutChain .right(15) .centerY(titleLab) historyBtn.sizeToFit() view.addSubview(collectionView) collectionView.layoutChain .topToBottomOfView(titleLab, offset: 15) .edgesHorzontal() .height(80) .bottom(15) return view }() lazy var collectionView: UICollectionView = { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .horizontal layout.itemSize = CGSize(width: 80, height: 80) layout.minimumLineSpacing = 15 layout.sectionInset = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15) let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) cv.backgroundColor = .clear cv.showsHorizontalScrollIndicator = false cv.register(ViewedCell.self) return cv }() /// 行程路线 lazy var travelRouteView: UIView = { let view = UIView() view.backgroundColor = .clear let titleLab = UILabel() titleLab.text = "行程路线" titleLab.font = .systemFont(ofSize: 16, weight: .medium) titleLab.textColor = ThemeManager.shared.color.titleAuxColor view.addSubview(titleLab) titleLab.layoutChain .top(15) .edgesVertical(5) let dotView = UIView() dotView.backgroundColor = UIColor(hexStr: "#16B3FF") dotView.cornerRadius = 2 view.addSubview(dotView) dotView.layoutChain .left(15) .centerY(titleLab) .width(4) .height(11) titleLab.layoutChain.leftToRightOfView(dotView, offset: 5) // 筛选 checkbox(从右到左) view.addSubview(allCheckBtn) view.addSubview(followedCheckBtn) view.addSubview(mineCheckBtn) allCheckBtn.layoutChain .right(15) .centerY(titleLab) followedCheckBtn.layoutChain .rightToLeftOfView(allCheckBtn, offset: -20) .centerY(titleLab) mineCheckBtn.layoutChain .rightToLeftOfView(followedCheckBtn, offset: -20) .centerY(titleLab) return view }() lazy var allCheckBtn: UIButton = makeCheckButton(title: "全部") lazy var followedCheckBtn: UIButton = makeCheckButton(title: "我关注的") lazy var mineCheckBtn: UIButton = makeCheckButton(title: "我的") private func makeCheckButton(title: String) -> UIButton { let btn = UIButton(type: .custom) btn.setImage(UIImage(named: "Schedule/checkbox"), for: .normal) btn.setImage(UIImage(named: "Schedule/checkbox_selected"), for: .selected) btn.setTitle(" \(title)", for: .normal) btn.setTitleColor(UIColor(hexStr: "#333333"), for: .normal) btn.titleLabel?.font = .systemFont(ofSize: 13, weight: .medium) btn.sizeToFit() return btn } lazy var tableView: UITableView = { let tableView = UITableView(frame: .zero, style: .plain) tableView.backgroundColor = .clear tableView.separatorStyle = .none tableView.estimatedRowHeight = 80 tableView.rowHeight = UITableView.automaticDimension tableView.showsVerticalScrollIndicator = false tableView.register(ScheduleListPopCell.self) tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 97 + kSafeBottomMargin, right: 0) return tableView }() lazy var createBtn: UIButton = { let btn = UIButton() btn.setImage(UIImage(named: "Schedule/create"), for: .normal) return btn }() override init(frame: CGRect) { super.init(frame: .zero) backgroundColor = .white setupUI() setupRx() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } // MARK: - ViewedCell final class ViewedCell: UICollectionViewCell { func configure(_ model: ViewedModel) { iconView.image = model.userIcon // 会员权益 blurView.isHidden = AppContextManager.shared.vip > 1 lockView.isHidden = AppContextManager.shared.vip > 1 viewedCountLab.text = "看了\(model.count)次" } lazy var iconView: UIImageView = { let iv = UIImageView() iv.contentMode = .scaleAspectFill iv.cornerRadius = 40 iv.clipsToBounds = true iv.backgroundColor = .clear iv.addSubview(blurView) blurView.layoutChain.edges() return iv }() lazy var blurView: UIVisualEffectView = { let blurEffect = UIBlurEffect(style: .systemUltraThinMaterial) let view = UIVisualEffectView(effect: blurEffect) return view }() lazy var lockView: UIView = { let view = UIView() view.backgroundColor = .clear let lockIcon = UIImageView(image: UIImage(named: "Common/lock_white")) lockIcon.contentMode = .scaleAspectFill view.addSubview(lockIcon) lockIcon.layoutChain .top(17) .centerX() view.addSubview(viewedCountLab) viewedCountLab.layoutChain .bottom(17) .edgesHorzontal() return view }() lazy var viewedCountLab: UILabel = { let label = UILabel() label.font = .systemFont(ofSize: 12, weight: .medium) label.textColor = .white label.textAlignment = .center return label }() override init(frame: CGRect) { super.init(frame: frame) contentView.addSubview(iconView) contentView.addSubview(lockView) iconView.layoutChain .edges() lockView.layoutChain .edges() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } // MARK: - ScheduleListPopCell class ScheduleListPopCell: UITableViewCell { var disposeBag = DisposeBag() func configure(_ model: ScheduleModel) { nameLab.text = model.nick_name + " 的行程路线" monthLab.text = getDateInterval2String(date: "\(model.timestamp/1000)", dateFormat: "MM月") editBtn.isHidden = !model.is_own followBtn.isHidden = model.is_own followBtn.isSelected = model.is_follow ? true : false let groupNames = model.groups.map { $0.group_name } // addTags 内部立即用 frame.width 排标签,先给个预估宽度避免算错 if !groupNames.isEmpty { let w = contentView.frame.width > 0 ? contentView.frame.width : UIScreen.main.bounds.width tagListView.frame.size.width = w - 170 } tagListView.removeAllTags() tagListView.addTags(groupNames) tagListView.tagViews.forEach { $0.layer.cornerRadius = 2 } tagListView.invalidateIntrinsicContentSize() guard let pointModel = model.points.last else { return } dateLab.text = getDateInterval2String(date: "\(pointModel.expected_timestamp/1000)", dateFormat: "MM.dd") } override init(style: CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none backgroundColor = .clear setupSubviews() } private func setupSubviews() { contentView.addSubview(bgView) bgView.addSubview(cornerView) cornerView.addSubview(nameLab) cornerView.addSubview(monthLab) cornerView.addSubview(dateLab) cornerView.addSubview(tagListView) cornerView.addSubview(editBtn) cornerView.addSubview(followBtn) bgView.layoutChain .top(15).bottom(15) .edgesHorzontal(15) cornerView.layoutChain.edges() monthLab.layoutChain .top(15) .left(11) .width(35) dateLab.layoutChain .topToBottomOfView(monthLab, offset: 4) .leftToView(monthLab) .width(35) editBtn.layoutChain .top(15) .right(10) .width(70) .height(28) nameLab.layoutChain .top(15) .leftToRightOfView(monthLab, offset: 15) tagListView.layoutChain .leftToRightOfView(dateLab, offset: 15) .bottom(15) .rightToLeftOfView(editBtn, offset: -10) nameLab.layoutChain.bottomToTopOfView(tagListView, offset:-5) followBtn.layoutChain .topToView(editBtn) .rightToView(editBtn) .width(70) .height(28) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func awakeFromNib() { super.awakeFromNib() // Initialization code } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) // Configure the view for the selected state } override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() } lazy var bgView: UIView = { let view = UIView() view.backgroundColor = .clear view.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.05).cgColor view.layer.shadowOffset = CGSize(width: 0, height: 0) view.layer.shadowOpacity = 1 view.layer.shadowRadius = 8 return view }() lazy var cornerView: UIView = { let view = UIView() view.backgroundColor = .white view.cornerRadius = 10 return view }() lazy var monthLab: UILabel = { let label = UILabel() label.textColor = ThemeManager.shared.color.titleAuxColor label.font = .systemFont(ofSize: 14, weight: .medium) return label }() lazy var dateLab: UILabel = { let label = UILabel() label.textColor = ThemeManager.shared.color.subTitleColor label.font = .systemFont(ofSize: 12, weight: .regular) return label }() lazy var nameLab: UILabel = { let label = UILabel() label.textColor = ThemeManager.shared.color.titleAuxColor label.font = .systemFont(ofSize: 14, weight: .medium) return label }() lazy var editBtn: UIButton = { let btn = UIButton() btn.isHidden = true btn.setTitle("编辑", for: .normal) btn.titleLabel?.font = .systemFont(ofSize: 12, weight: .medium) btn.setTitleColor(UIColor(hexStr: "#0F2846"), for: .normal) btn.backgroundColor = UIColor(hexStr: "#EEFAFF") btn.borderWidth = 0.5 btn.borderColor = UIColor(hexStr: "#16B3FF") btn.cornerRadius = 14 return btn }() lazy var followBtn: UIButton = { let btn = UIButton() btn.isHidden = true btn.setTitle("关注", for: .normal) btn.setTitle("取消关注", for: .selected) btn.titleLabel?.font = .systemFont(ofSize: 12, weight: .medium) btn.setTitleColor(UIColor(hexStr: "#16B3FF"), for: .normal) btn.backgroundColor = .white btn.borderWidth = 0.5 btn.borderColor = UIColor(hexStr: "#16B3FF") btn.cornerRadius = 14 return btn }() lazy var tagListView: TagListView = { let view = TagListView() view.textFont = UIFont.systemFont(ofSize: 8, weight: .regular) view.textColor = UIColor(hexStr: "#B78C56") view.tagBackgroundColor = UIColor(hexStr: "#FFF3E4") view.paddingX = 6 // 水平内边距 view.paddingY = 6 // 垂直内边距 view.alignment = .left // 对齐 view.translatesAutoresizingMaskIntoConstraints = false return view }() }