// // MemberPhoneReportView.swift // QuickLocation // import UIKit struct MemberPhoneReportPreview { let screenTimeSeconds: Int? let usageCount: Int let unlockCount: Int static func currentUser(from manager: UnlockCountManager = .shared) -> MemberPhoneReportPreview { let seconds = manager.todayScreenTimeSeconds return MemberPhoneReportPreview( screenTimeSeconds: seconds > 0 ? seconds : nil, usageCount: manager.todayAppUsageCount, unlockCount: manager.todayCount ) } } /// 成员面板「手机报告」:屏幕使用时长 + 使用次数 + 今日解锁 final class MemberPhoneReportView: UIView { private enum Layout { static let designCardWidth: CGFloat = 345 static let leftPanelWidthRatio: CGFloat = 164 / designCardWidth static let leftPanelAspect: CGFloat = 147 / 164 static let panelGap: CGFloat = 13 static let cardInset: CGFloat = 12 static let usageCardAspect: CGFloat = 69 / 146 static let usageUnlockGap: CGFloat = 9 } private let titleColor = UIColor(hexStr: "#293445") private let usageTint = UIColor(hexStr: "#E8FBE8") private let unlockTint = UIColor(hexStr: "#E8F4FF") private let sectionIcon = UIImageView() private let sectionTitleLab = UILabel() private let cardView = UIView() private let leftPanel = UIView() private let rightPanel = UIView() private let screenTimeIcon = UIImageView() private let screenTimeTitleLab = UILabel() private let screenTimeBadge = ScreenTimeBadgeView() private let usageCard = UIView() private let usageIcon = UIImageView() private let usageTitleLab = UILabel() private let usageValueLab = UILabel() private let unlockCard = UIView() private let unlockIcon = UIImageView() private let unlockTitleLab = UILabel() private let unlockValueLab = UILabel() override init(frame: CGRect) { super.init(frame: frame) setupUI() configure(with: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func configure(with preview: MemberPhoneReportPreview?) { if let preview { screenTimeBadge.configure(seconds: preview.screenTimeSeconds) usageValueLab.text = "\(preview.usageCount)" unlockValueLab.text = "\(preview.unlockCount)" } else { screenTimeBadge.configure(seconds: nil) usageValueLab.text = "**" unlockValueLab.text = "**" } } private func setupUI() { backgroundColor = .clear sectionIcon.image = UIImage(named: "Home/member_phone_report") sectionIcon.contentMode = .scaleAspectFit addSubview(sectionIcon) sectionIcon.layoutChain .top() .left(15) .width(18) .heightToWidth(1) sectionTitleLab.text = "手机报告" sectionTitleLab.font = .systemFont(ofSize: 16, weight: .bold) sectionTitleLab.textColor = titleColor addSubview(sectionTitleLab) sectionTitleLab.layoutChain .leftToRightOfView(sectionIcon, offset: 6) .centerY(sectionIcon) cardView.backgroundColor = .white cardView.cornerRadius = 16 addSubview(cardView) cardView.layoutChain .topToBottomOfView(sectionIcon, offset: 12) .edgesHorzontal(15) .bottom() cardView.addSubview(leftPanel) cardView.addSubview(rightPanel) leftPanel.backgroundColor = UIColor(hexStr: "#FFFCEA") leftPanel.cornerRadius = 14 rightPanel.backgroundColor = .clear leftPanel.layoutChain .top(Layout.cardInset) .left(Layout.cardInset) .widthToView(cardView, multiplier: Layout.leftPanelWidthRatio) .heightToWidth(Layout.leftPanelAspect) rightPanel.layoutChain .top(Layout.cardInset) .right(Layout.cardInset) .leftToRightOfView(leftPanel, offset: Layout.panelGap) .heightToView(leftPanel) .bottom(Layout.cardInset) screenTimeIcon.image = UIImage(named: "Home/member_report_clock") screenTimeIcon.contentMode = .scaleAspectFit leftPanel.addSubview(screenTimeIcon) screenTimeIcon.layoutChain .top(7) .left(7) .width(20) .height(20) screenTimeTitleLab.text = "屏幕使用时长" screenTimeTitleLab.font = .systemFont(ofSize: 12, weight: .bold) screenTimeTitleLab.textColor = UIColor(hexStr: "#293445") leftPanel.addSubview(screenTimeTitleLab) screenTimeTitleLab.layoutChain .leftToRightOfView(screenTimeIcon, offset: 6) .centerY(screenTimeIcon) leftPanel.addSubview(screenTimeBadge) screenTimeBadge.layoutChain .topToBottomOfView(screenTimeTitleLab, offset: 13) .centerX(leftPanel, offset: 7) let screenTimeBadgeArrow = UIImageView(image: UIImage(named: "Home/member_screenTime_arrow")) leftPanel.addSubview(screenTimeBadgeArrow) screenTimeBadgeArrow.layoutChain .topToBottomOfView(screenTimeBadge) .centerX(screenTimeBadge) let barchartIcon = UIImageView(image: UIImage(named: "Home/member_screenTime")) leftPanel.addSubview(barchartIcon) barchartIcon.layoutChain .centerX() .topToBottomOfView(screenTimeBadge, offset: 12) usageCard.backgroundColor = UIColor(hexStr: "#F8FFEE") usageCard.cornerRadius = 14 rightPanel.addSubview(usageCard) usageCard.layoutChain .top() .edgesHorzontal() .heightToWidth(Layout.usageCardAspect) usageIcon.image = UIImage(named: "Home/member_usage_count") usageIcon.contentMode = .scaleAspectFit usageCard.addSubview(usageIcon) usageIcon.layoutChain .top(8) .left(10) .width(20) .height(20) usageTitleLab.text = "使用次数" usageTitleLab.font = .systemFont(ofSize: 12, weight: .bold) usageTitleLab.textColor = UIColor(hexStr: "#293445") usageCard.addSubview(usageTitleLab) usageTitleLab.layoutChain .leftToRightOfView(usageIcon, offset: 6) .centerY(usageIcon) .right(relation: .lessThanOrEqual) usageValueLab.font = .systemFont(ofSize: 24, weight: .bold) usageValueLab.textColor = UIColor(hexStr: "#83DE00") usageCard.addSubview(usageValueLab) usageValueLab.layoutChain .topToBottomOfView(usageTitleLab, offset: 4) .centerX() let usageValueUnitLab = UILabel() usageValueUnitLab.text = "次" usageValueUnitLab.textColor = UIColor(hexStr: "#293445") usageValueUnitLab.font = .systemFont(ofSize: 12, weight: .bold) usageCard.addSubview(usageValueUnitLab) usageValueUnitLab.layoutChain .leftToRightOfView(usageValueLab, offset: 3) .bottomToView(usageValueLab, offset: -4) unlockCard.backgroundColor = UIColor(hexStr: "#F2FEFF") unlockCard.cornerRadius = 14 rightPanel.addSubview(unlockCard) unlockCard.layoutChain .topToBottomOfView(usageCard, offset: Layout.usageUnlockGap) .edgesHorzontal() .heightToWidth(Layout.usageCardAspect) .bottom() unlockIcon.image = UIImage(named: "Home/member_daily_unlock") unlockIcon.contentMode = .scaleAspectFit unlockCard.addSubview(unlockIcon) unlockIcon.layoutChain .top(8) .left(10) .width(20) .height(20) unlockTitleLab.text = "今日解锁" unlockTitleLab.font = .systemFont(ofSize: 12, weight: .bold) unlockTitleLab.textColor = UIColor(hexStr: "#293445") unlockCard.addSubview(unlockTitleLab) unlockTitleLab.layoutChain .leftToRightOfView(unlockIcon, offset: 6) .centerY(unlockIcon) .right(relation: .lessThanOrEqual) unlockValueLab.font = .systemFont(ofSize: 24, weight: .bold) unlockValueLab.textColor = UIColor(hexStr: "#08B6C4") unlockCard.addSubview(unlockValueLab) unlockValueLab.layoutChain .topToBottomOfView(unlockTitleLab, offset: 4) .centerX() let unlockValueUnitLab = UILabel() unlockValueUnitLab.text = "次" unlockValueUnitLab.textColor = UIColor(hexStr: "#293445") unlockValueUnitLab.font = .systemFont(ofSize: 12, weight: .bold) unlockCard.addSubview(unlockValueUnitLab) unlockValueUnitLab.layoutChain .leftToRightOfView(unlockValueLab, offset: 3) .bottomToView(unlockValueLab, offset: -4) } } // MARK: - 屏幕时长 Badge private final class ScreenTimeBadgeView: UIView { private let contentStack = UIStackView() private let numberColor = UIColor(hexStr: "#FF6B4A") private let unitColor = UIColor(hexStr: "#293445") private let numberFont = UIFont.systemFont(ofSize: 24, weight: .heavy) private let unitFont = UIFont.systemFont(ofSize: 14, weight: .heavy) override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .white cornerRadius = 11 contentStack.axis = .horizontal contentStack.alignment = .lastBaseline contentStack.spacing = 2 addSubview(contentStack) contentStack.layoutChain .edges(UIEdgeInsets(top: 6, left: 10, bottom: 6, right: 10)) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func configure(seconds: Int?) { contentStack.arrangedSubviews.forEach { view in contentStack.removeArrangedSubview(view) view.removeFromSuperview() } guard let seconds, seconds > 0 else { contentStack.addArrangedSubview(makeLabel(text: "**", font: numberFont, color: numberColor)) return } let hours = seconds / 3600 let minutes = (seconds % 3600) / 60 if hours > 0 { appendPart(number: "\(hours)", unit: "h") if minutes > 0 { contentStack.addArrangedSubview(makeSpacer(width: 4)) appendPart(number: "\(minutes)", unit: "m") } } else { appendPart(number: "\(minutes)", unit: "m") } } private func appendPart(number: String, unit: String) { contentStack.addArrangedSubview(makeLabel(text: number, font: numberFont, color: numberColor)) contentStack.addArrangedSubview(makeLabel(text: unit, font: unitFont, color: unitColor)) } private func makeLabel(text: String, font: UIFont, color: UIColor) -> UILabel { let label = UILabel() label.text = text label.font = font label.textColor = color return label } private func makeSpacer(width: CGFloat) -> UIView { let spacer = UIView() spacer.translatesAutoresizingMaskIntoConstraints = false spacer.widthAnchor.constraint(equalToConstant: width).isActive = true return spacer } }