// // PowerPopupView.swift // RoderickRalph // // Created by Neoa on 2025/8/20. // import Foundation import UIKit struct LoginUserInfo { let nickName: String let userId: String let registryTimeStr: String let todayAnswerCount: Int let historyAnswerCount: Int let headImgURL: String? let lastLoginTimeStr: String let answerLogs: [String] // ✅ 新增:作答时间记录 } protocol PowerPopupViewDelegate: AnyObject { func powerPopupViewDidTapGetPower(_ view: PowerPopupView) func powerPopupViewDidClose(_ view: PowerPopupView) } class PowerPopupView: UIView, UITableViewDelegate { weak var delegate: PowerPopupViewDelegate? // MARK: - UI Components private let backgroundView: UIView = { let view = UIView() view.backgroundColor = UIColor.black.withAlphaComponent(0.5) // Semi-transparent gray background view.translatesAutoresizingMaskIntoConstraints = false return view }() private let timeLabel: UILabel = { let label = UILabel() label.text = "" label.font = UIFont.systemFont(ofSize: 14) label.textColor = .white label.translatesAutoresizingMaskIntoConstraints = false return label }() private let redBGView: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "tqan325") iv.contentMode = .scaleToFill iv.translatesAutoresizingMaskIntoConstraints = false return iv }() private let whiteBGView: UIView = { let view = UIView() view.backgroundColor = .white view.layer.cornerRadius = 12 view.translatesAutoresizingMaskIntoConstraints = false return view }() private let redFontView: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "tqan324") iv.contentMode = .scaleToFill iv.translatesAutoresizingMaskIntoConstraints = false return iv }() private let softwareNameLabel: UILabel = { let label = UILabel() label.text = "青柠檬记账" label.font = UIFont.systemFont(ofSize: 16) label.textColor = .white label.translatesAutoresizingMaskIntoConstraints = false return label }() private let countDownLabel: UILabel = { let label = UILabel() label.text = "7s" label.font = UIFont.systemFont(ofSize: 24) label.textColor = .white label.translatesAutoresizingMaskIntoConstraints = false return label }() private let getPowerButton: UIButton = { let button = UIButton(type: .system) button.setBackgroundImage(UIImage(named: "tqan323"), for: .normal) button.setTitle("获取体力", for: .normal) button.setTitleColor(.white, for: .normal) button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 24) button.contentHorizontalAlignment = .center button.translatesAutoresizingMaskIntoConstraints = false button.isHidden = true // Initially hidden return button }() private let userInfoBGView: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "tqan326") iv.contentMode = .scaleToFill iv.translatesAutoresizingMaskIntoConstraints = false return iv }() private let iconBGView: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "tqan331") iv.contentMode = .scaleAspectFit iv.translatesAutoresizingMaskIntoConstraints = false return iv }() private let iconImageView: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "catlogos") iv.contentMode = .scaleAspectFit iv.layer.cornerRadius = 24 iv.clipsToBounds = true iv.translatesAutoresizingMaskIntoConstraints = false return iv }() private let nikNmeLeftLabel: UILabel = { let label = UILabel() label.text = "昵 称" label.font = UIFont.systemFont(ofSize: 12) label.textColor = UIColor(hexString:"#E28814") label.translatesAutoresizingMaskIntoConstraints = false return label }() private let nikNmeRightBGView: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "tqan332") iv.contentMode = .scaleToFill iv.translatesAutoresizingMaskIntoConstraints = false return iv }() private let nikNmeRightLabel: UILabel = { let label = UILabel() label.text = "XXXX" label.font = UIFont.systemFont(ofSize: 12) label.textColor = UIColor(hexString:"#E28814") label.textAlignment = .center label.translatesAutoresizingMaskIntoConstraints = false return label }() private let roleIDLeftLabel: UILabel = { let label = UILabel() label.text = "角色ID" label.font = UIFont.systemFont(ofSize: 12) label.textColor = UIColor(hexString:"#E28814") label.translatesAutoresizingMaskIntoConstraints = false return label }() private let roleIDRightBGView: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "tqan332") iv.contentMode = .scaleToFill iv.translatesAutoresizingMaskIntoConstraints = false return iv }() private let roleIDRightLabel: UILabel = { let label = UILabel() label.text = "XXXX" label.font = UIFont.systemFont(ofSize: 12) label.textColor = UIColor(hexString:"#E28814") if let image = UIImage(named: "tqan332") { label.layer.contents = image.cgImage label.layer.contentsGravity = .resizeAspect // 控制图片显示方式 label.layer.contentsScale = UIScreen.main.scale } label.textAlignment = .center label.translatesAutoresizingMaskIntoConstraints = false return label }() private let registrationTimeLabel: UILabel = { let label = UILabel() label.text = "注册时间: 2025-05-26 17:58:48" label.font = UIFont.systemFont(ofSize: 12) label.textColor = UIColor(hexString:"#E28814") label.translatesAutoresizingMaskIntoConstraints = false return label }() private let loginTimeLabel: UILabel = { let label = UILabel() label.text = "登录时间: 2025-05-26 17:58:48" label.font = UIFont.systemFont(ofSize: 12) label.textColor = UIColor(hexString:"#E28814") label.translatesAutoresizingMaskIntoConstraints = false return label }() private let todayAnswerLabel: UILabel = { let label = UILabel() label.text = "今日答题: XX题" label.font = UIFont.boldSystemFont(ofSize: 12) label.textColor = UIColor(hexString:"#E28814") label.translatesAutoresizingMaskIntoConstraints = false return label }() private let historyAnswerLabel: UILabel = { let label = UILabel() label.text = "历史答题: XX题" label.font = UIFont.boldSystemFont(ofSize: 12) label.textColor = UIColor(hexString:"#E28814") label.translatesAutoresizingMaskIntoConstraints = false return label }() private let answerLogBGView: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "tqan326") iv.contentMode = .scaleToFill iv.translatesAutoresizingMaskIntoConstraints = false return iv }() private let answerLogLabel: UILabel = { let label = UILabel() label.text = "记录" label.font = UIFont.boldSystemFont(ofSize: 12) label.textColor = UIColor(hexString:"#E28814") label.translatesAutoresizingMaskIntoConstraints = false return label }() private let logDivider: UIView = { let view = UIView() view.backgroundColor = UIColor(hexString: "#EEEEEE") view.translatesAutoresizingMaskIntoConstraints = false return view }() private let answerLogListView: UITableView = { let tableView = UITableView() tableView.translatesAutoresizingMaskIntoConstraints = false tableView.isScrollEnabled = false tableView.isScrollEnabled = true // Enable scrolling if records exceed visible area tableView.backgroundColor = .clear // Transparent background tableView.separatorStyle = .none // No separators between rows tableView.rowHeight = 20 // Adjust the row height if necessary return tableView }() private let closeButton: UIButton = { let button = UIButton(type: .custom) button.setImage(UIImage(named: "pqan334"), for: .normal) button.addTarget(self, action: #selector(closePopup), for: .touchUpInside) button.translatesAutoresizingMaskIntoConstraints = false button.isHidden = true // Initially hidden return button }() private var countdownTimer: Timer? private var secondsRemaining = 7 private var hasRecordedOpenTime = false private var answerLogs: [String] = [] // ✅ 来自登录返回 answerRecordTimeList // MARK: - Initialization init() { super.init(frame: .zero) // Register the cell class for the table view answerLogListView.register(UITableViewCell.self, forCellReuseIdentifier: "AnswerLogCell") answerLogListView.dataSource = self answerLogListView.delegate = self setupUI() setupConstraints() setOpenTime() startCountdown() // Start countdown as soon as the view is initialized } override func didMoveToWindow() { super.didMoveToWindow() if window != nil && !hasRecordedOpenTime { setOpenTime() hasRecordedOpenTime = true } } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - UI Setup private func setupUI() { addSubview(backgroundView) addSubview(timeLabel) addSubview(redBGView) addSubview(whiteBGView) addSubview(userInfoBGView) addSubview(iconBGView) addSubview(iconImageView) addSubview(nikNmeLeftLabel) addSubview(nikNmeRightBGView) addSubview(nikNmeRightLabel) addSubview(roleIDLeftLabel) addSubview(roleIDRightBGView) addSubview(roleIDRightLabel) addSubview(registrationTimeLabel) addSubview(loginTimeLabel) addSubview(todayAnswerLabel) addSubview(historyAnswerLabel) addSubview(answerLogBGView) addSubview(answerLogLabel) addSubview(logDivider) addSubview(answerLogListView) addSubview(closeButton) addSubview(redFontView) addSubview(softwareNameLabel) addSubview(countDownLabel) addSubview(getPowerButton) // Prefill software name with selected channel if available applySavedChannelName() // 让“获取体力”按钮把点击事件回传给外部 getPowerButton.addTarget(self, action: #selector(handleGetPowerButtonTap), for: .touchUpInside) } func configure(with info: LoginUserInfo) { // Sync software/app name with selected channel applySavedChannelName() nikNmeRightLabel.text = info.nickName roleIDRightLabel.text = info.userId registrationTimeLabel.text = "注册时间: \(info.registryTimeStr)" loginTimeLabel.text = "登录时间: \(info.lastLoginTimeStr)" todayAnswerLabel.text = "今日答题: \(info.todayAnswerCount)题" historyAnswerLabel.text = "历史答题: \(info.historyAnswerCount)题" if let urlStr = info.headImgURL, let url = URL(string: urlStr) { loadImage(into: iconImageView, from: url) } // ✅ 写入答题记录并刷新列表 answerLogs = info.answerLogs answerLogListView.reloadData() } // Helper to apply saved channel name to softwareNameLabel private func applySavedChannelName() { if let savedChannel = UserDefaults.standard.string(forKey: "selectedChannelName"), !savedChannel.isEmpty { softwareNameLabel.text = savedChannel } } private static let imageCache = NSCache() private func loadImage(into imageView: UIImageView, from url: URL) { if let cached = PowerPopupView.imageCache.object(forKey: url as NSURL) { imageView.image = cached return } URLSession.shared.dataTask(with: url) { data, _, _ in guard let data = data, let img = UIImage(data: data) else { return } PowerPopupView.imageCache.setObject(img, forKey: url as NSURL) DispatchQueue.main.async { imageView.image = img } }.resume() } @objc private func handleGetPowerButtonTap() { delegate?.powerPopupViewDidTapGetPower(self) } private func setupConstraints() { NSLayoutConstraint.activate([ // Background backgroundView.topAnchor.constraint(equalTo: topAnchor), backgroundView.bottomAnchor.constraint(equalTo: bottomAnchor), backgroundView.leadingAnchor.constraint(equalTo: leadingAnchor), backgroundView.trailingAnchor.constraint(equalTo: trailingAnchor), // User Info Container timeLabel.topAnchor.constraint(equalTo: topAnchor, constant: 142), timeLabel.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0), redBGView.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0), redBGView.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0), redBGView.widthAnchor.constraint(equalToConstant: 331), redBGView.heightAnchor.constraint(equalToConstant: 300), whiteBGView.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0), whiteBGView.bottomAnchor.constraint(equalTo: redBGView.bottomAnchor, constant: -90), whiteBGView.widthAnchor.constraint(equalToConstant: 317), whiteBGView.heightAnchor.constraint(equalToConstant: 307), redFontView.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0), redFontView.bottomAnchor.constraint(equalTo: redBGView.bottomAnchor, constant: 0), redFontView.widthAnchor.constraint(equalToConstant: 331), redFontView.heightAnchor.constraint(equalToConstant: 133), softwareNameLabel.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0), softwareNameLabel.topAnchor.constraint(equalTo: redFontView.topAnchor, constant: 43), countDownLabel.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0), countDownLabel.bottomAnchor.constraint(equalTo: redFontView.bottomAnchor, constant: -20), getPowerButton.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0), getPowerButton.bottomAnchor.constraint(equalTo: redFontView.bottomAnchor, constant: -20), getPowerButton.widthAnchor.constraint(equalToConstant: 221), getPowerButton.heightAnchor.constraint(equalToConstant: 47), userInfoBGView.topAnchor.constraint(equalTo: whiteBGView.topAnchor, constant: 14), userInfoBGView.leadingAnchor.constraint(equalTo: whiteBGView.leadingAnchor, constant: 14), userInfoBGView.trailingAnchor.constraint(equalTo: whiteBGView.trailingAnchor, constant: -14), userInfoBGView.heightAnchor.constraint(equalToConstant: 133), iconBGView.topAnchor.constraint(equalTo: userInfoBGView.topAnchor, constant: 12), iconBGView.leadingAnchor.constraint(equalTo: userInfoBGView.leadingAnchor, constant: 16), iconBGView.widthAnchor.constraint(equalToConstant: 54), iconBGView.heightAnchor.constraint(equalToConstant: 54), iconImageView.centerXAnchor.constraint(equalTo: iconBGView.centerXAnchor, constant: 0), iconImageView.centerYAnchor.constraint(equalTo: iconBGView.centerYAnchor, constant: 0), iconImageView.widthAnchor.constraint(equalToConstant: 46), iconImageView.heightAnchor.constraint(equalToConstant: 46), nikNmeRightBGView.trailingAnchor.constraint(equalTo: userInfoBGView.trailingAnchor,constant: -16), nikNmeRightBGView.topAnchor.constraint(equalTo: userInfoBGView.topAnchor,constant: 14), nikNmeRightBGView.widthAnchor.constraint(equalToConstant: 130), nikNmeRightBGView.heightAnchor.constraint(equalToConstant: 24), nikNmeRightLabel.trailingAnchor.constraint(equalTo: userInfoBGView.trailingAnchor,constant: -16), nikNmeRightLabel.topAnchor.constraint(equalTo: userInfoBGView.topAnchor,constant: 14), nikNmeRightLabel.widthAnchor.constraint(equalToConstant: 130), nikNmeRightLabel.heightAnchor.constraint(equalToConstant: 24), nikNmeLeftLabel.trailingAnchor.constraint(equalTo: nikNmeRightLabel.leadingAnchor,constant: -5), nikNmeLeftLabel.centerYAnchor.constraint(equalTo: nikNmeRightLabel.centerYAnchor,constant: 0), roleIDRightBGView.trailingAnchor.constraint(equalTo: nikNmeRightLabel.trailingAnchor,constant: 0), roleIDRightBGView.topAnchor.constraint(equalTo: nikNmeRightLabel.bottomAnchor,constant: 3), roleIDRightBGView.widthAnchor.constraint(equalToConstant: 130), roleIDRightBGView.heightAnchor.constraint(equalToConstant: 24), roleIDRightLabel.trailingAnchor.constraint(equalTo: nikNmeRightLabel.trailingAnchor,constant: 0), roleIDRightLabel.topAnchor.constraint(equalTo: nikNmeRightLabel.bottomAnchor,constant: 3), roleIDRightLabel.widthAnchor.constraint(equalToConstant: 130), roleIDRightLabel.heightAnchor.constraint(equalToConstant: 24), roleIDLeftLabel.trailingAnchor.constraint(equalTo: roleIDRightLabel.leadingAnchor,constant: -5), roleIDLeftLabel.centerYAnchor.constraint(equalTo: roleIDRightLabel.centerYAnchor,constant: 0), registrationTimeLabel.topAnchor.constraint(equalTo: iconBGView.bottomAnchor,constant: 15), registrationTimeLabel.leadingAnchor.constraint(equalTo: iconBGView.leadingAnchor,constant: 0), loginTimeLabel.topAnchor.constraint(equalTo: registrationTimeLabel.bottomAnchor,constant: 3), loginTimeLabel.leadingAnchor.constraint(equalTo: iconBGView.leadingAnchor,constant: 0), todayAnswerLabel.topAnchor.constraint(equalTo: userInfoBGView.bottomAnchor,constant: 12), todayAnswerLabel.leadingAnchor.constraint(equalTo: userInfoBGView.leadingAnchor,constant: 31), historyAnswerLabel.topAnchor.constraint(equalTo: userInfoBGView.bottomAnchor,constant: 12), historyAnswerLabel.trailingAnchor.constraint(equalTo: userInfoBGView.trailingAnchor,constant: -31), answerLogBGView.topAnchor.constraint(equalTo: todayAnswerLabel.bottomAnchor,constant: 12), answerLogBGView.leadingAnchor.constraint(equalTo: userInfoBGView.leadingAnchor,constant: 0), answerLogBGView.trailingAnchor.constraint(equalTo: userInfoBGView.trailingAnchor,constant: 0), answerLogBGView.heightAnchor.constraint(equalToConstant: 90), answerLogLabel.centerYAnchor.constraint(equalTo: answerLogBGView.centerYAnchor,constant: 0), answerLogLabel.leadingAnchor.constraint(equalTo: answerLogBGView.leadingAnchor,constant: 18), logDivider.topAnchor.constraint(equalTo: answerLogBGView.topAnchor,constant: 8), logDivider.bottomAnchor.constraint(equalTo: answerLogBGView.bottomAnchor,constant: -8), logDivider.leadingAnchor.constraint(equalTo: answerLogLabel.trailingAnchor,constant: 13), logDivider.widthAnchor.constraint(equalToConstant: 1), answerLogListView.topAnchor.constraint(equalTo: answerLogBGView.topAnchor,constant: 5), answerLogListView.bottomAnchor.constraint(equalTo: answerLogBGView.bottomAnchor,constant: -5), answerLogListView.leadingAnchor.constraint(equalTo: logDivider.trailingAnchor,constant: 28), answerLogListView.trailingAnchor.constraint(equalTo: answerLogBGView.trailingAnchor,constant: -30), closeButton.topAnchor.constraint(equalTo: whiteBGView.topAnchor,constant: -12), closeButton.trailingAnchor.constraint(equalTo: whiteBGView.trailingAnchor,constant: 12), closeButton.heightAnchor.constraint(equalToConstant: 24), closeButton.widthAnchor.constraint(equalToConstant: 24), ]) } // MARK: - Countdown Logic private func startCountdown() { countDownLabel.text = "\(secondsRemaining)s" countdownTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateCountdown), userInfo: nil, repeats: true) } @objc private func updateCountdown() { secondsRemaining -= 1 countDownLabel.text = "\(secondsRemaining)s" if secondsRemaining == 0 { countdownTimer?.invalidate() // Stop the countdown countdownTimer = nil showButtons() // Show the buttons once countdown reaches 0 } } private func showButtons() { getPowerButton.isHidden = false // Show the 'Get Power' button closeButton.isHidden = false // Show the 'Close' button countDownLabel.isHidden = true } // MARK: - Actions @objc private func closePopup() { delegate?.powerPopupViewDidClose(self) removeFromSuperview() } private func setOpenTime() { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" formatter.locale = Locale(identifier: "zh_CN") formatter.timeZone = .current timeLabel.text = formatter.string(from: Date()) } } extension PowerPopupView: UITableViewDataSource { // Return number of rows (the number of records) func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.answerLogs.count // ✅ 改这里 } // Configure each cell to display the record func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "AnswerLogCell") ?? UITableViewCell(style: .default, reuseIdentifier: "AnswerLogCell") cell.selectionStyle = .none // Disable selection highlight // Display the answer log in each row cell.textLabel?.text = self.answerLogs[indexPath.row] // ✅ 改这里 cell.textLabel?.font = UIFont.systemFont(ofSize: 12) cell.textLabel?.textColor = UIColor(hexString: "#E28814") cell.backgroundColor = .clear // Transparent background for each cell return cell } }