// // MineView.swift // QuickLocation // // Created based on Lanhu design: 我的·版本6 / 照片墙改版 // import UIKit import RxSwift import RxCocoa final class MineView: UIView { var disposeBag = DisposeBag() private let vipCardHeight: CGFloat = 90 private let settingsRowHeight: CGFloat = 52 private let quickItemTitles = ["权限检测", "数据安全", "状态设置", "隐私设置"] var onQuickTap: ((String) -> Void)? func setupData(_ model: UserConfigModel) { guard AppContextManager.shared.systemConfig?.isIntercept == false else { maskBgView.isHidden = false return } maskBgView.isHidden = true guard model.vip > 1 else { vipTitleLab.text = "开通会员" vipContentLab.text = "会员尊享10+VIP特权" vipActivateButton.isHidden = false vipActivateButton.setTitle("立即开通", for: .normal) return } vipTitleLab.text = model.vip_name vipContentLab.text = model.vip == 3 ? "会员终身有效" : "会员到期:\(self.getDateInterval2String(date: model.vip_expire_time, dateFormat: "yyyy.MM.dd"))" vipActivateButton.setTitle("点击续费", for: .normal) vipActivateButton.isHidden = model.vip == 3 } // MARK: - Components private lazy var scrollView: UIScrollView = { let sv = UIScrollView() sv.backgroundColor = UIColor(hexStr: "#FAFAFA") sv.showsVerticalScrollIndicator = false // sv.alwaysBounceVertical = true return sv }() private lazy var contentView: UIView = { let v = UIView() v.backgroundColor = .clear return v }() private lazy var headerBgView: UIView = { let v = UIView() v.backgroundColor = .clear return v }() lazy var photoWall: MinePhotoWallView = { MinePhotoWallView() }() lazy var vipCardView: UIView = { let view = UIView() view.layer.cornerRadius = 16 view.clipsToBounds = true return view }() lazy var vipCardBgImageView: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "Mine/vip_card") iv.contentMode = .scaleToFill return iv }() lazy var maskBgView: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "Mine/mask_bg") iv.contentMode = .scaleToFill iv.isHidden = true return iv }() lazy var vipTitleLab: UILabel = { let label = UILabel() label.font = FontManager.boboBold(18) label.textColor = .white label.text = " " return label }() lazy var vipContentLab: UILabel = { let label = UILabel() label.font = .systemFont(ofSize: 12, weight: .medium) label.textColor = .white label.text = " " return label }() lazy var vipActivateButton: UIButton = { let btn = UIButton(type: .custom) btn.backgroundColor = UIColor(hexStr: "#C0FF54") btn.setTitle("立即开通", for: .normal) btn.setTitleColor(UIColor(hexStr: "#00343B"), for: .normal) btn.titleLabel?.font = FontManager.boboBold(14) btn.cornerRadius = 9 btn.isHidden = true return btn }() private lazy var quickCardView: UIView = { let v = UIView() v.backgroundColor = .white v.cornerRadius = 20 return v }() private lazy var quickStack: UIStackView = { let stack = UIStackView() stack.axis = .horizontal stack.distribution = .fillEqually stack.alignment = .center return stack }() private lazy var settingsCardView: UIView = { let view = UIView() view.backgroundColor = .white view.layer.cornerRadius = 12 view.layer.shadowColor = UIColor(red: 0.898, green: 0.898, blue: 0.898, alpha: 0.3).cgColor view.layer.shadowOffset = .zero view.layer.shadowRadius = 4 view.layer.shadowOpacity = 1 return view }() lazy var settingsTableView: UITableView = { let tv = UITableView(frame: .zero, style: .plain) tv.backgroundColor = .clear tv.isScrollEnabled = false tv.separatorStyle = .none tv.register(MineMenuCell.self) tv.rowHeight = settingsRowHeight tv.estimatedRowHeight = settingsRowHeight return tv }() private var settingsCardHeightConstraint: NSLayoutConstraint? private var contentSizeObservation: NSKeyValueObservation? // 兼容旧绑定(已迁到 photoWall) lazy var avatarImageView: UIImageView = UIImageView() lazy var nameLabel: UILabel = UILabel() lazy var idLabel: UILabel = UILabel() lazy var editButton: UIButton = UIButton(type: .custom) lazy var copyButton: UIButton = UIButton(type: .custom) override init(frame: CGRect) { super.init(frame: frame) setupUI() setupLayout() setupQuickActions() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { backgroundColor = UIColor(hexStr: "#FAFAFA") addSubview(scrollView) scrollView.addSubview(contentView) contentView.addSubview(headerBgView) // 照片墙固定顶部,不随列表滚动 addSubview(photoWall) contentView.addSubview(vipCardView) vipCardView.addSubview(vipCardBgImageView) vipCardView.addSubview(vipActivateButton) vipCardView.addSubview(vipTitleLab) vipCardView.addSubview(vipContentLab) vipCardView.addSubview(maskBgView) contentView.addSubview(quickCardView) quickCardView.addSubview(quickStack) contentView.addSubview(settingsCardView) settingsCardView.addSubview(settingsTableView) } private func setupLayout() { photoWall.layoutChain .top(40) .edgesHorzontal() scrollView.layoutChain .topToBottomOfView(photoWall) .edgesHorzontal() .bottom() contentView.layoutChain .edges() .widthToView(scrollView) vipCardView.layoutChain .top(12) .left(16).right(16) .height(vipCardHeight) vipCardBgImageView.layoutChain.edges() vipTitleLab.layoutChain .top(22) .left(77) vipContentLab.layoutChain .topToBottomOfView(vipTitleLab, offset: 6) .leftToView(vipTitleLab) vipActivateButton.layoutChain .right(19).centerY() .width(70).height(28) maskBgView.layoutChain.edges() quickCardView.layoutChain .topToBottomOfView(vipCardView, offset: 20) .left(16).right(16) .height(90) quickStack.layoutChain .edges(all: 8) settingsCardView.layoutChain .topToBottomOfView(quickCardView, offset: 16) .left(16).right(16) .bottom(20) settingsCardHeightConstraint = settingsCardView.heightAnchor.constraint(equalToConstant: 208) settingsCardHeightConstraint?.isActive = true settingsTableView.layoutChain.edges() contentSizeObservation = settingsTableView.observe(\.contentSize, options: [.new, .initial]) { [weak self] _, _ in self?.refreshTableViewHeight() } } private func setupQuickActions() { let icons = [ "Mine/quick_permission", "Mine/quick_data_security", "Mine/quick_status", "Mine/quick_privacy" ] for (i, title) in quickItemTitles.enumerated() { let item = makeQuickItem(icon: icons[i], title: title) item.tag = i item.rx.tapGesture .subscribe(onNext: { [weak self] _ in self?.onQuickTap?(title) }) .disposed(by: disposeBag) quickStack.addArrangedSubview(item) } } private func makeQuickItem(icon: String, title: String) -> UIView { let wrap = UIView() wrap.isUserInteractionEnabled = true let view = UIView() view.backgroundColor = UIColor(hexStr: "#F3F5F8") view.cornerRadius = 16 let iv = UIImageView(image: UIImage(named: icon)) iv.contentMode = .scaleAspectFill view.addSubview(iv) let lab = UILabel() lab.text = title lab.font = .systemFont(ofSize: 12, weight: .medium) lab.textColor = UIColor(hexStr: "#293445") lab.textAlignment = .center wrap.addSubview(view) wrap.addSubview(lab) view.layoutChain .top(8) .centerX() .width(40) .height(40) iv.layoutChain .centerY() .centerX() .width(24) .height(24) lab.layoutChain .topToBottomOfView(view, offset: 6) .edgesHorzontal(2) .bottom(4) return wrap } override func layoutSubviews() { super.layoutSubviews() // 顶部淡色渐变氛围 setGradientLayer( frame: CGRect(x: 0, y: 0, width: bounds.width, height: min(280, bounds.height * 0.35)), startPoint: CGPoint(x: 0.5, y: 0), endPoint: CGPoint(x: 0.5, y: 1), colors: [UIColor(hexStr: "#D8F4FF"), UIColor(hexStr: "#FAFAFA")], locations: [0, 1] ) refreshTableViewHeight() } func refreshTableViewHeight() { let contentHeight = settingsTableView.contentSize.height guard contentHeight > 0 else { return } settingsCardHeightConstraint?.constant = contentHeight settingsTableView.isScrollEnabled = false } } // MARK: - MineMenuCell final class MineMenuCell: UITableViewCell { func configure(_ model: MineMenuItem) { iconImageView.image = UIImage(named: model.icon) titleLabel.text = model.title } private func setupSubviews() { contentView.addSubview(iconImageView) contentView.addSubview(titleLabel) contentView.addSubview(arrowImageView) contentView.addSubview(dividerLine) iconImageView.layoutChain .left(14).centerY() .size(CGSize(width: 24, height: 24)) titleLabel.layoutChain .leftToRightOfView(iconImageView, offset: 8) .centerY() arrowImageView.layoutChain .right(14).centerY() .size(CGSize(width: 18, height: 18)) dividerLine.layoutChain .left(14).right().bottom() .height(0.5) } var showDivider: Bool = true { didSet { dividerLine.isHidden = !showDivider } } private let iconImageView: UIImageView = { let iv = UIImageView() iv.contentMode = .scaleAspectFit return iv }() private let titleLabel: UILabel = { let label = UILabel() label.font = UIFont(name: "PingFangSC-Medium", size: 14) ?? UIFont.systemFont(ofSize: 14, weight: .medium) label.textColor = UIColor(hexStr: "#1A1A1A") return label }() private let arrowImageView: UIImageView = { let iv = UIImageView() iv.contentMode = .scaleAspectFit iv.image = UIImage(named: "Mine/arrow_right") return iv }() private let dividerLine: UIView = { let view = UIView() view.backgroundColor = UIColor(hexStr: "#EDEDED") return view }() override init(style: CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) backgroundColor = .clear selectionStyle = .none setupSubviews() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }