| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568 |
- //
- // 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<NSURL, UIImage>()
- 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
- }
-
- }
|