// // OrderCardCell.swift // suixinkan // import SnapKit import UIKit struct OrderCardActions { var onRefund: (() -> Void)? var onVerify: (() -> Void)? var onUploadTask: (() -> Void)? var onCancel: (() -> Void)? var onCallPhone: (() -> Void)? var onGiftPhotoMinus: (() -> Void)? var onGiftPhotoPlus: (() -> Void)? var onGiftVideoMinus: (() -> Void)? var onGiftVideoPlus: (() -> Void)? var onSendGift: (() -> Void)? var onRefinedChanged: ((Int) -> Void)? var onHistoricalPhoto: (() -> Void)? var onOrderSourceTap: (() -> Void)? } /// 摄影师订单卡片,对齐 Android `OrderView`。 final class OrderCardCell: UITableViewCell { static let reuseIdentifier = "OrderCardCell" private let cardView = UIView() private let orderNumberLabel = UILabel() private let statusBadge = OrderStatusBadgeView() private let createdAtLabel = UILabel() private let typeChip = UILabel() private let actionStack = UIStackView() private let refundButton = UIButton(type: .system) private let verifyButton = UIButton(type: .system) private let uploadTaskButton = UIButton(type: .system) private let infoStack = UIStackView() private let cancelButton = UIButton(type: .system) private let orderSourceButton = UIButton(type: .system) private let historicalButton = UIButton(type: .system) private let refinedStack = UIStackView() private let refinedNeedButton = UIButton(type: .system) private let refinedNoButton = UIButton(type: .system) private var actions = OrderCardActions() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none backgroundColor = .clear contentView.backgroundColor = .clear setupUI() } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func configure(order: OrderEntity, hideWriteActions: Bool, actions: OrderCardActions, showOrderSource: Bool, sourceText: String?) { self.actions = actions orderNumberLabel.text = "订单号:\(order.orderNumber)" statusBadge.apply(status: order.orderStatus, text: order.orderStatusName) createdAtLabel.text = order.createdAt.isEmpty ? "--" : order.createdAt typeChip.text = order.orderTypeLabel typeChip.isHidden = order.orderTypeLabel.isEmpty let showRefund = !hideWriteActions && (order.orderStatus == 18 || order.orderStatus == 30) && order.orderType != 19 let showVerify = !hideWriteActions && ( (order.photoTravel?.needCheckIn == true && order.orderStatus != 30) || order.orderType == 19 ) refundButton.isHidden = !showRefund verifyButton.isHidden = !showVerify actionStack.isHidden = !showRefund && !showVerify uploadTaskButton.isHidden = hideWriteActions || order.orderType != 19 orderSourceButton.isHidden = !showOrderSource orderSourceButton.setTitle(sourceText ?? "选择订单来源", for: .normal) let showRefined = order.orderStatus == 18 || order.orderStatus == 30 refinedStack.isHidden = !showRefined updateRefinedButtons(isRefined: order.isRefined, readOnly: hideWriteActions) cancelButton.isHidden = hideWriteActions || order.photoTravel?.canCancelOrder != true infoStack.arrangedSubviews.forEach { $0.removeFromSuperview() } addInfoRow(title: "付款金额", value: "¥\(order.actualPayAmount)") if !order.payTime.isEmpty { addInfoRow(title: "付款时间", value: order.payTime) } if !order.completeTime.isEmpty { addInfoRow(title: "完成时间", value: order.completeTime) } if !order.payTypeName.isEmpty { addInfoRow(title: "付款方式", value: order.payTypeName) } addInfoRow(title: "UID", value: String(order.userId)) addPhoneRow(phone: order.phone, hidden: hideWriteActions) if let travel = order.photoTravel, !travel.checkInTime.isEmpty { addInfoRow(title: "核销时间", value: travel.checkInTime) } if let travel = order.photoTravel { addInfoRow(title: "修图张数", value: "\(travel.orderPhotoNum)") addInfoRow(title: "视频", value: "\(travel.orderVideoNum)") } if let remark = order.remark, !remark.isEmpty { addInfoRow(title: "备注", value: remark) } let materials = order.multiTravel?.materialList ?? [] historicalButton.isHidden = materials.isEmpty } private func setupUI() { cardView.backgroundColor = .white cardView.layer.cornerRadius = 12 orderNumberLabel.font = .systemFont(ofSize: 14) orderNumberLabel.textColor = .black createdAtLabel.font = .systemFont(ofSize: 12) createdAtLabel.textColor = UIColor(hex: 0x6B7280) typeChip.font = .systemFont(ofSize: 12) typeChip.textColor = AppColor.primary typeChip.backgroundColor = UIColor(hex: 0xEFF6FF) typeChip.layer.cornerRadius = 4 typeChip.clipsToBounds = true typeChip.textAlignment = .center configureActionButton(refundButton, title: "退款", color: UIColor(hex: 0xEF4444), bg: UIColor(hex: 0xFFE7E7)) configureActionButton(verifyButton, title: "核销订单", color: .white, bg: UIColor(hex: 0x0073FF)) refundButton.addTarget(self, action: #selector(refundTapped), for: .touchUpInside) verifyButton.addTarget(self, action: #selector(verifyTapped), for: .touchUpInside) uploadTaskButton.setTitle("上传任务", for: .normal) uploadTaskButton.setTitleColor(AppColor.primary, for: .normal) uploadTaskButton.titleLabel?.font = .systemFont(ofSize: 16) uploadTaskButton.backgroundColor = UIColor(hex: 0xEFF6FF) uploadTaskButton.layer.cornerRadius = 12 uploadTaskButton.addTarget(self, action: #selector(uploadTaskTapped), for: .touchUpInside) actionStack.axis = .horizontal actionStack.spacing = 12 actionStack.distribution = .fillEqually actionStack.addArrangedSubview(refundButton) actionStack.addArrangedSubview(verifyButton) infoStack.axis = .vertical infoStack.spacing = 6 cancelButton.setTitle("取消订单", for: .normal) cancelButton.setTitleColor(UIColor(hex: 0xEF4444), for: .normal) cancelButton.titleLabel?.font = .systemFont(ofSize: 14) cancelButton.addTarget(self, action: #selector(cancelTapped), for: .touchUpInside) orderSourceButton.setTitleColor(AppColor.primary, for: .normal) orderSourceButton.titleLabel?.font = .systemFont(ofSize: 14) orderSourceButton.contentHorizontalAlignment = .leading orderSourceButton.addTarget(self, action: #selector(orderSourceTapped), for: .touchUpInside) historicalButton.setTitle("历史拍摄", for: .normal) historicalButton.setTitleColor(AppColor.primary, for: .normal) historicalButton.titleLabel?.font = .systemFont(ofSize: 14) historicalButton.addTarget(self, action: #selector(historicalTapped), for: .touchUpInside) refinedStack.axis = .horizontal refinedStack.spacing = 8 refinedNeedButton.setTitle("需要精修", for: .normal) refinedNoButton.setTitle("无需精修", for: .normal) [refinedNeedButton, refinedNoButton].forEach { $0.titleLabel?.font = .systemFont(ofSize: 13) $0.layer.cornerRadius = 6 $0.layer.borderWidth = 1 } refinedNeedButton.addTarget(self, action: #selector(refinedNeedTapped), for: .touchUpInside) refinedNoButton.addTarget(self, action: #selector(refinedNoTapped), for: .touchUpInside) refinedStack.addArrangedSubview(refinedNeedButton) refinedStack.addArrangedSubview(refinedNoButton) contentView.addSubview(cardView) cardView.addSubview(orderNumberLabel) cardView.addSubview(statusBadge) cardView.addSubview(createdAtLabel) cardView.addSubview(typeChip) cardView.addSubview(orderSourceButton) cardView.addSubview(actionStack) cardView.addSubview(uploadTaskButton) cardView.addSubview(refinedStack) cardView.addSubview(infoStack) cardView.addSubview(historicalButton) cardView.addSubview(cancelButton) cardView.snp.makeConstraints { make in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 16, bottom: 12, right: 16)) } orderNumberLabel.snp.makeConstraints { make in make.top.leading.equalToSuperview().inset(16) } statusBadge.snp.makeConstraints { make in make.centerY.equalTo(orderNumberLabel) make.trailing.equalToSuperview().inset(16) } createdAtLabel.snp.makeConstraints { make in make.top.equalTo(orderNumberLabel.snp.bottom).offset(4) make.leading.equalToSuperview().inset(16) } typeChip.snp.makeConstraints { make in make.top.equalTo(createdAtLabel.snp.bottom).offset(4) make.leading.equalToSuperview().inset(16) make.height.equalTo(20) make.width.greaterThanOrEqualTo(40) } orderSourceButton.snp.makeConstraints { make in make.top.equalTo(typeChip.snp.bottom).offset(8) make.leading.trailing.equalToSuperview().inset(16) } actionStack.snp.makeConstraints { make in make.top.equalTo(orderSourceButton.snp.bottom).offset(12) make.leading.trailing.equalToSuperview().inset(16) make.height.equalTo(48) } uploadTaskButton.snp.makeConstraints { make in make.top.equalTo(actionStack.snp.bottom).offset(12) make.leading.trailing.equalToSuperview().inset(16) make.height.equalTo(48) } refinedStack.snp.makeConstraints { make in make.top.equalTo(uploadTaskButton.snp.bottom).offset(12) make.leading.equalToSuperview().inset(16) } infoStack.snp.makeConstraints { make in make.top.equalTo(refinedStack.snp.bottom).offset(12) make.leading.trailing.equalToSuperview().inset(16) } historicalButton.snp.makeConstraints { make in make.top.equalTo(infoStack.snp.bottom).offset(8) make.leading.equalToSuperview().inset(16) } cancelButton.snp.makeConstraints { make in make.top.equalTo(historicalButton.snp.bottom).offset(12) make.leading.equalToSuperview().inset(16) make.bottom.equalToSuperview().inset(16) } } private func configureActionButton(_ button: UIButton, title: String, color: UIColor, bg: UIColor) { button.setTitle(title, for: .normal) button.setTitleColor(color, for: .normal) button.titleLabel?.font = .systemFont(ofSize: 16) button.backgroundColor = bg button.layer.cornerRadius = 8 } private func addInfoRow(title: String, value: String) { let row = UIStackView() row.axis = .horizontal row.distribution = .fillEqually let titleLabel = UILabel() titleLabel.text = title titleLabel.font = .systemFont(ofSize: 13) titleLabel.textColor = UIColor(hex: 0x6B7280) let valueLabel = UILabel() valueLabel.text = value valueLabel.font = .systemFont(ofSize: 13) valueLabel.textColor = AppColor.text333 valueLabel.textAlignment = .right row.addArrangedSubview(titleLabel) row.addArrangedSubview(valueLabel) infoStack.addArrangedSubview(row) } private func addPhoneRow(phone: String, hidden: Bool) { let row = UIStackView() row.axis = .horizontal row.distribution = .fillEqually let titleLabel = UILabel() titleLabel.text = "手机号" titleLabel.font = .systemFont(ofSize: 13) titleLabel.textColor = UIColor(hex: 0x6B7280) let valueButton = UIButton(type: .system) valueButton.setTitle(OrderPhoneMask.mask(phone), for: .normal) valueButton.setTitleColor(AppColor.primary, for: .normal) valueButton.titleLabel?.font = .systemFont(ofSize: 13) valueButton.contentHorizontalAlignment = .trailing valueButton.isHidden = hidden || phone.isEmpty valueButton.addTarget(self, action: #selector(callTapped), for: .touchUpInside) row.addArrangedSubview(titleLabel) row.addArrangedSubview(valueButton) infoStack.addArrangedSubview(row) } private func updateRefinedButtons(isRefined: Int, readOnly: Bool) { let selectedBorder = AppColor.primary.cgColor let normalBorder = UIColor(hex: 0xE5E7EB).cgColor refinedNeedButton.layer.borderColor = isRefined == 1 ? selectedBorder : normalBorder refinedNoButton.layer.borderColor = isRefined == 2 ? selectedBorder : normalBorder refinedNeedButton.isEnabled = !readOnly refinedNoButton.isEnabled = !readOnly } @objc private func refundTapped() { actions.onRefund?() } @objc private func verifyTapped() { actions.onVerify?() } @objc private func uploadTaskTapped() { actions.onUploadTask?() } @objc private func cancelTapped() { actions.onCancel?() } @objc private func callTapped() { actions.onCallPhone?() } @objc private func orderSourceTapped() { actions.onOrderSourceTap?() } @objc private func historicalTapped() { actions.onHistoricalPhoto?() } @objc private func refinedNeedTapped() { actions.onRefinedChanged?(1) } @objc private func refinedNoTapped() { actions.onRefinedChanged?(2) } } enum OrderPhoneMask { static func mask(_ phone: String) -> String { let trimmed = phone.trimmingCharacters(in: .whitespacesAndNewlines) guard trimmed.count >= 7 else { return trimmed } let start = trimmed.prefix(3) let end = trimmed.suffix(4) return "\(start)****\(end)" } }