// // LNOrderRecordCell.swift // Lanu // // Created by OneeChan on 2025/12/24. // import Foundation import UIKit import SnapKit class LNOrderRecordCell: UITableViewCell { private let dateLabel = UILabel() private let stateLabel = UILabel() private let gameIc = UIImageView() private let gameNameLabel = UILabel() private let orderIdLabel = UILabel() private let discountView = UIView() private let totalLabel = UILabel() private let countLabel = UILabel() private let avatar = UIImageView() private let nameLabel = UILabel() private let genderView = UIImageView() private let requestLine = UIView() private let requestView = UIView() private let requestLabel = UILabel() private let menuView = UIView() private let rejectButton = UIButton() private let acceptButton = UIButton() private let startButton = UIButton() private let completeButton = UIButton() private let commitButton = UIButton() private var curItem: LNOrderRecordItemVO? override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupViews() } func update(_ item: LNOrderRecordItemVO) { gameIc.sd_setImage(with: URL(string: item.categoryIcon)) dateLabel.text = TimeInterval(item.createTime / 1_000).formattedFullDateWithTime() gameNameLabel.text = item.bizCategoryName orderIdLabel.text = item.orderId discountView.isHidden = item.totalAmount >= item.price * Double(item.purchaseQty) totalLabel.text = item.totalAmount.toDisplay countLabel.text = "\(item.unit) x \(item.purchaseQty)" avatar.showAvatar(item.avatar) nameLabel.text = item.nickname genderView.image = switch item.gender { case .unknow: nil case .male: .icGenderMale case .female: .icGenderFemale } requestLabel.text = item.customerRemark requestLine.isHidden = item.customerRemark.isEmpty requestView.isHidden = item.customerRemark.isEmpty curItem = item updateByStateChanged() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension LNOrderRecordCell { private func updateByStateChanged() { guard let curItem else { return } menuView.isHidden = true rejectButton.isHidden = true acceptButton.isHidden = true startButton.isHidden = true completeButton.isHidden = true commitButton.isHidden = true if curItem.refundApply { stateLabel.text = .init(key: "A00052") stateLabel.textColor = .init(hex: "#F23051") menuView.isHidden = false commitButton.isHidden = false return } switch curItem.status { case .created, .waitingForAccept: stateLabel.text = .init(key: "A00055") stateLabel.textColor = .init(hex: "#FFB836") menuView.isHidden = false rejectButton.isHidden = false acceptButton.isHidden = false case .completed: stateLabel.text = .init(key: "A00137") stateLabel.textColor = .text_6 case .refunded: stateLabel.text = .init(key: "A00138") stateLabel.textColor = .text_3 case .accepted: stateLabel.text = .init(key: "A00165") stateLabel.textColor = .text_6 menuView.isHidden = false startButton.isHidden = false case .rejected: stateLabel.text = .init(key: "A00140") stateLabel.textColor = .text_3 case .servicing: stateLabel.text = .init(key: "A00080") stateLabel.textColor = .init(hex: "#FFB836") menuView.isHidden = false completeButton.isHidden = false case .serviceDone: stateLabel.text = .init(key: "A00137") stateLabel.textColor = .text_6 case .cancelled: stateLabel.text = .init(key: "A00152") stateLabel.textColor = .text_3 } } private func setupViews() { backgroundColor = .clear let container = UIView() container.backgroundColor = .fill container.layer.cornerRadius = 12 contentView.addSubview(container) container.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(16) make.top.equalToSuperview().offset(10) make.bottom.equalToSuperview() } let stackView = UIStackView() stackView.axis = .vertical stackView.spacing = 0 container.addSubview(stackView) stackView.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(12) make.top.equalToSuperview().offset(2) make.bottom.equalToSuperview().offset(-8).priority(.medium) } let top = buildTopView() stackView.addArrangedSubview(top) let line = UIView() line.backgroundColor = .fill_2 line.snp.makeConstraints { make in make.height.equalTo(0.5).priority(.medium) } stackView.addArrangedSubview(line) let orderInfo = buildOrderInfo() stackView.addArrangedSubview(orderInfo) let userInfo = buildUserInfoView() stackView.addArrangedSubview(userInfo) let menu = buildBottomView() stackView.addArrangedSubview(menu) } private func buildTopView() -> UIView { let container = UIView() container.snp.makeConstraints { make in make.height.equalTo(30) } stateLabel.font = .heading_h5 container.addSubview(stateLabel) stateLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() make.trailing.equalToSuperview() make.width.height.equalTo(0).priority(.low) } dateLabel.font = .body_xs dateLabel.textColor = .text_3 container.addSubview(dateLabel) dateLabel.snp.makeConstraints { make in make.leading.equalToSuperview() make.centerY.equalToSuperview() make.trailing.lessThanOrEqualTo(stateLabel.snp.leading).offset(-5) } return container } private func buildOrderInfo() -> UIView { let container = UIView() container.snp.makeConstraints { make in make.height.equalTo(54) } let price = buildPriceView() container.addSubview(price) price.snp.makeConstraints { make in make.centerY.equalToSuperview() make.trailing.equalToSuperview() } let infoView = buildGameInfoView() container.addSubview(infoView) infoView.snp.makeConstraints { make in make.leading.equalToSuperview() make.centerY.equalToSuperview().inset(4) make.trailing.lessThanOrEqualTo(price.snp.leading).offset(-12) } return container } private func buildUserInfoView() -> UIView { let container = UIView() container.backgroundColor = .fill_1 container.layer.cornerRadius = 12 let stackView = UIStackView() stackView.axis = .vertical stackView.spacing = 4 container.addSubview(stackView) stackView.snp.makeConstraints { make in make.edges.equalToSuperview().inset(8) } let userView = UIView() userView.onTap { [weak self] in guard let self else { return } guard let curItem else { return } pushToProfile(uid: curItem.buyerUserNo) } userView.snp.makeConstraints { make in make.height.equalTo(23).priority(.medium) } stackView.addArrangedSubview(userView) avatar.layer.cornerRadius = 11.5 avatar.clipsToBounds = true userView.addSubview(avatar) avatar.snp.makeConstraints { make in make.leading.equalToSuperview() make.centerY.equalToSuperview() make.width.height.equalTo(23) } nameLabel.font = .body_s nameLabel.textColor = .text_5 userView.addSubview(nameLabel) nameLabel.snp.makeConstraints { make in make.centerY.equalTo(avatar) make.leading.equalTo(avatar.snp.trailing).offset(8) } userView.addSubview(genderView) genderView.snp.makeConstraints { make in make.centerY.equalTo(avatar) make.leading.equalTo(nameLabel.snp.trailing).offset(3) make.width.height.equalTo(14) } requestLine.backgroundColor = .fill_3 requestLine.snp.makeConstraints { make in make.height.equalTo(0.5) } stackView.addArrangedSubview(requestLine) stackView.addArrangedSubview(requestView) let titleLabel = UILabel() titleLabel.font = .heading_h5 titleLabel.textColor = .text_4 titleLabel.text = .init(key: "A00167") requestView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.leading.equalToSuperview() make.top.equalToSuperview() } requestLabel.font = .body_xs requestLabel.textColor = .text_4 requestLabel.numberOfLines = 0 requestLabel.setContentHuggingPriority(.defaultHigh, for: .vertical) requestLabel.setContentCompressionResistancePriority(.defaultHigh , for: .vertical) requestView.addSubview(requestLabel) requestLabel.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview() make.top.equalTo(titleLabel.snp.bottom) make.bottom.equalToSuperview() } return container } private func buildBottomView() -> UIView { menuView.snp.makeConstraints { make in make.height.equalTo(38) } let stackView = UIStackView() stackView.axis = .horizontal stackView.spacing = 6 menuView.addSubview(stackView) stackView.snp.makeConstraints { make in make.trailing.equalToSuperview() make.bottom.equalToSuperview() } rejectButton.setTitle(.init(key: "A00168"), for: .normal) rejectButton.setTitleColor(.text_3, for: .normal) rejectButton.titleLabel?.font = .heading_h5 rejectButton.layer.cornerRadius = 15 rejectButton.layer.borderWidth = 1 rejectButton.layer.borderColor = UIColor.fill_3.cgColor rejectButton.contentEdgeInsets = .init(top: 8, left: 18, bottom: 8, right: 18) rejectButton.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } guard let curItem else { return } LNOrderManager.shared.rejectOrder(orderId: curItem.orderId) { [weak self] success in guard let self else { return } guard success else { return } curItem.status = .rejected updateByStateChanged() } }), for: .touchUpInside) rejectButton.snp.makeConstraints { make in make.height.equalTo(30) } stackView.addArrangedSubview(rejectButton) acceptButton.setTitle(.init(key: "A00169"), for: .normal) acceptButton.setTitleColor(.text_1, for: .normal) acceptButton.titleLabel?.font = .heading_h5 acceptButton.layer.cornerRadius = 15 acceptButton.setBackgroundImage(.primary_8, for: .normal) acceptButton.contentEdgeInsets = .init(top: 8, left: 10, bottom: 8, right: 10) acceptButton.clipsToBounds = true acceptButton.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } guard let curItem else { return } LNOrderManager.shared.acceptOrder(orderId: curItem.orderId) { [weak self] success in guard let self else { return } guard success else { return } curItem.status = .accepted updateByStateChanged() } }), for: .touchUpInside) acceptButton.snp.makeConstraints { make in make.height.equalTo(30) } stackView.addArrangedSubview(acceptButton) startButton.setTitle(.init(key: "A00064"), for: .normal) startButton.setTitleColor(.text_1, for: .normal) startButton.titleLabel?.font = .heading_h5 startButton.layer.cornerRadius = 15 startButton.setBackgroundImage(.primary_8, for: .normal) startButton.contentEdgeInsets = .init(top: 8, left: 10, bottom: 8, right: 10) startButton.clipsToBounds = true startButton.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } guard let curItem else { return } LNOrderManager.shared.startOrderService(orderId: curItem.orderId) { [weak self] success in guard let self else { return } guard success else { return } curItem.status = .servicing updateByStateChanged() } }), for: .touchUpInside) startButton.snp.makeConstraints { make in make.height.equalTo(30) } stackView.addArrangedSubview(startButton) completeButton.setTitle(.init(key: "A00151"), for: .normal) completeButton.setTitleColor(.text_1, for: .normal) completeButton.titleLabel?.font = .heading_h5 completeButton.layer.cornerRadius = 15 completeButton.setBackgroundImage(.primary_8, for: .normal) completeButton.contentEdgeInsets = .init(top: 8, left: 10, bottom: 8, right: 10) completeButton.clipsToBounds = true completeButton.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } guard let curItem else { return } LNOrderManager.shared.finishOrderService(orderId: curItem.orderId) { [weak self] success in guard let self else { return } guard success else { return } curItem.status = .serviceDone updateByStateChanged() } }), for: .touchUpInside) completeButton.snp.makeConstraints { make in make.height.equalTo(30) } stackView.addArrangedSubview(completeButton) commitButton.setTitle(.init(key: "A00170"), for: .normal) commitButton.setTitleColor(.text_6, for: .normal) commitButton.titleLabel?.font = .heading_h5 commitButton.layer.cornerRadius = 15 commitButton.setBackgroundImage(.primary_7, for: .normal) commitButton.contentEdgeInsets = .init(top: 8, left: 10, bottom: 8, right: 10) commitButton.clipsToBounds = true commitButton.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } guard let curItem else { return } pushToProtest(curItem.orderId) { [weak self] in guard let self else { return } curItem.status = .serviceDone updateByStateChanged() } }), for: .touchUpInside) commitButton.snp.makeConstraints { make in make.height.equalTo(30) } stackView.addArrangedSubview(commitButton) let cover = UIView() cover.backgroundColor = .fill cover.layer.cornerRadius = 14 cover.isUserInteractionEnabled = false commitButton.insertSubview(cover, at: 0) cover.snp.makeConstraints { make in make.edges.equalToSuperview().inset(1) } return menuView } private func buildGameInfoView() -> UIView { let container = UIView() gameIc.layer.cornerRadius = 20 gameIc.clipsToBounds = true container.addSubview(gameIc) gameIc.snp.makeConstraints { make in make.leading.equalToSuperview() make.verticalEdges.equalToSuperview() make.width.height.equalTo(50) } let textInfo = UIView() container.addSubview(textInfo) textInfo.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalTo(gameIc.snp.trailing).offset(8) make.trailing.equalToSuperview() } gameNameLabel.textColor = .text_5 gameNameLabel.font = .heading_h4 textInfo.addSubview(gameNameLabel) gameNameLabel.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview() make.top.equalToSuperview() } orderIdLabel.font = .body_xs orderIdLabel.textColor = .text_5 textInfo.addSubview(orderIdLabel) orderIdLabel.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview() make.bottom.equalToSuperview() make.top.equalTo(gameNameLabel.snp.bottom).offset(2) } return container } private func buildPriceView() -> UIView { let container = UIView() totalLabel.font = .heading_h3 totalLabel.textColor = .text_5 container.addSubview(totalLabel) totalLabel.snp.makeConstraints { make in make.top.trailing.equalToSuperview() } let coin = UIImageView.coinImageView() container.addSubview(coin) coin.snp.makeConstraints { make in make.centerY.equalTo(totalLabel) make.trailing.equalTo(totalLabel.snp.leading) make.width.height.equalTo(16) } let discount = buildDiscount() container.addSubview(discount) discount.snp.makeConstraints { make in make.centerY.equalTo(coin) make.leading.equalToSuperview() make.trailing.equalTo(coin.snp.leading).offset(-4) } countLabel.font = .body_m countLabel.textColor = .text_5 countLabel.textAlignment = .right countLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) countLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) container.addSubview(countLabel) countLabel.snp.makeConstraints { make in make.trailing.bottom.equalToSuperview() make.top.equalTo(totalLabel.snp.bottom) } return container } private func buildDiscount() -> UIView { discountView.layer.cornerRadius = 4 discountView.backgroundColor = .init(hex: "#FF6F32") let titleLabel = UILabel() titleLabel.text = .init(key: "A00129") titleLabel.font = .body_xs titleLabel.textColor = .text_1 discountView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(5) make.verticalEdges.equalToSuperview().inset(2) } return discountView } } #if DEBUG import SwiftUI struct LNOrderRecordCellPreview: UIViewRepresentable { func makeUIView(context: Context) -> some UIView { let container = UIView() container.backgroundColor = .lightGray let view = LNOrderRecordCell() container.addSubview(view) view.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview() make.centerY.equalToSuperview() make.height.equalTo(133) } return container } func updateUIView(_ uiView: UIViewType, context: Context) { } } #Preview(body: { LNOrderRecordCellPreview() }) #endif // DEBUG