607 lines
26 KiB
Swift
607 lines
26 KiB
Swift
import SnapKit
|
||
import UIKit
|
||
|
||
/// 日清列表条目。
|
||
private enum OfflineDailyItem: Hashable {
|
||
case record(OfflineCollectionRecord)
|
||
case empty(String)
|
||
}
|
||
|
||
/// 日清记录在白色列表卡中的圆角位置。
|
||
private enum OfflineRecordPosition {
|
||
case single
|
||
case first
|
||
case middle
|
||
case last
|
||
}
|
||
|
||
/// 线下收款日清页,按 9.7 视觉稿展示日历、2×2 汇总、明细和补缴操作。
|
||
@MainActor
|
||
final class OfflineCollectionDailyViewController: BaseViewController {
|
||
private let viewModel: OfflineCollectionDailyViewModel
|
||
private let tableView = UITableView(frame: .zero, style: .plain)
|
||
private var dataSource: UITableViewDiffableDataSource<Int, OfflineDailyItem>!
|
||
private let headerContainer = UIView()
|
||
private let headerStack = UIStackView()
|
||
private let calendarView = OfflineCollectionCalendarView()
|
||
private let summaryCard = UIView()
|
||
private let summaryDateLabel = UILabel()
|
||
private let totalValue = UILabel()
|
||
private let countValue = UILabel()
|
||
private let settledValue = UILabel()
|
||
private let pendingValue = UILabel()
|
||
private let pendingStatusLabel = UILabel()
|
||
private let statusButton = UIButton(type: .system)
|
||
private let bottomContainer = UIView()
|
||
private let settlementButton = OfflineCollectionGradientButton(
|
||
startColor: UIColor(hex: 0xFF8B00),
|
||
endColor: UIColor(hex: 0xFF7200)
|
||
)
|
||
private var feedbackPresented = false
|
||
private var isShowingGlobalLoading = false
|
||
|
||
init(
|
||
businessDate: String,
|
||
context: OfflineCollectionContext = .current(),
|
||
api: any OfflineCollectionServing = NetworkServices.shared.offlineCollectionAPI
|
||
) {
|
||
viewModel = OfflineCollectionDailyViewModel(businessDate: businessDate, context: context, api: api)
|
||
super.init(nibName: nil, bundle: nil)
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||
|
||
override func setupNavigationBar() {
|
||
title = "线下收款日清"
|
||
}
|
||
|
||
override func setupUI() {
|
||
view.backgroundColor = UIColor(hex: 0xF7FAFF)
|
||
configureTableView()
|
||
configureHeader()
|
||
configureBottomBar()
|
||
|
||
view.addSubview(tableView)
|
||
view.addSubview(bottomContainer)
|
||
tableView.snp.makeConstraints { make in
|
||
make.top.leading.trailing.equalTo(view.safeAreaLayoutGuide)
|
||
make.bottom.equalTo(bottomContainer.snp.top)
|
||
}
|
||
bottomContainer.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview() }
|
||
}
|
||
|
||
override func bindActions() {
|
||
calendarView.onDateSelected = { [weak self] date in
|
||
guard let self else { return }
|
||
Task { await self.viewModel.selectBusinessDate(date) }
|
||
}
|
||
calendarView.onModeChanged = { [weak self] in self?.resizeHeader() }
|
||
viewModel.onStateChange = { [weak self] in Task { @MainActor in self?.applyState() } }
|
||
}
|
||
|
||
override func viewWillAppear(_ animated: Bool) {
|
||
super.viewWillAppear(animated)
|
||
Task { await viewModel.refresh() }
|
||
}
|
||
|
||
override func viewWillDisappear(_ animated: Bool) {
|
||
super.viewWillDisappear(animated)
|
||
setGlobalLoadingVisible(false)
|
||
}
|
||
|
||
override func viewDidLayoutSubviews() {
|
||
super.viewDidLayoutSubviews()
|
||
resizeHeader()
|
||
}
|
||
|
||
private func configureTableView() {
|
||
tableView.backgroundColor = .clear
|
||
tableView.separatorStyle = .none
|
||
tableView.rowHeight = UITableView.automaticDimension
|
||
tableView.estimatedRowHeight = 82
|
||
tableView.showsVerticalScrollIndicator = false
|
||
tableView.contentInset.bottom = 12
|
||
tableView.register(OfflineCollectionRecordCell.self, forCellReuseIdentifier: OfflineCollectionRecordCell.reuseIdentifier)
|
||
tableView.register(OfflineCollectionEmptyCell.self, forCellReuseIdentifier: OfflineCollectionEmptyCell.reuseIdentifier)
|
||
dataSource = makeDataSource()
|
||
}
|
||
|
||
private func configureHeader() {
|
||
headerStack.axis = .vertical
|
||
headerStack.spacing = 16
|
||
headerContainer.addSubview(headerStack)
|
||
headerStack.snp.makeConstraints { make in
|
||
make.top.equalToSuperview().offset(12)
|
||
make.bottom.equalToSuperview().inset(10)
|
||
make.leading.trailing.equalToSuperview().inset(16)
|
||
}
|
||
headerStack.addArrangedSubview(calendarView)
|
||
configureSummaryCard()
|
||
headerStack.addArrangedSubview(summaryCard)
|
||
|
||
let listTitle = UILabel()
|
||
listTitle.text = "收款明细"
|
||
listTitle.font = .systemFont(ofSize: 18, weight: .bold)
|
||
listTitle.textColor = UIColor(hex: 0x081739)
|
||
let listTitleContainer = UIView()
|
||
listTitleContainer.addSubview(listTitle)
|
||
listTitle.snp.makeConstraints { make in
|
||
make.leading.equalToSuperview().offset(8)
|
||
make.trailing.centerY.equalToSuperview()
|
||
}
|
||
listTitleContainer.snp.makeConstraints { make in make.height.equalTo(42) }
|
||
headerStack.setCustomSpacing(18, after: summaryCard)
|
||
headerStack.addArrangedSubview(listTitleContainer)
|
||
|
||
statusButton.configuration = .plain()
|
||
statusButton.configuration?.baseForegroundColor = AppColor.primary
|
||
statusButton.addTarget(self, action: #selector(retryTapped), for: .touchUpInside)
|
||
statusButton.snp.makeConstraints { make in make.height.greaterThanOrEqualTo(44) }
|
||
statusButton.isHidden = true
|
||
headerStack.addArrangedSubview(statusButton)
|
||
tableView.tableHeaderView = headerContainer
|
||
}
|
||
|
||
private func configureSummaryCard() {
|
||
summaryCard.backgroundColor = .white
|
||
summaryCard.layer.cornerRadius = 12
|
||
summaryCard.layer.shadowColor = UIColor(hex: 0x8FA0B8).cgColor
|
||
summaryCard.layer.shadowOpacity = 0.12
|
||
summaryCard.layer.shadowRadius = 10
|
||
summaryCard.layer.shadowOffset = CGSize(width: 0, height: 4)
|
||
|
||
summaryDateLabel.font = .systemFont(ofSize: 18, weight: .bold)
|
||
summaryDateLabel.textColor = UIColor(hex: 0x081739)
|
||
|
||
let topLeft = metric(title: "线下收款总额", value: totalValue)
|
||
let topRight = metric(title: "登记笔数", value: countValue)
|
||
let bottomLeft = metric(title: "已补缴", value: settledValue)
|
||
let bottomRight = metric(title: "待补缴", value: pendingValue, badge: pendingStatusLabel)
|
||
let grid = UIView()
|
||
[topLeft, topRight, bottomLeft, bottomRight].forEach(grid.addSubview)
|
||
topLeft.snp.makeConstraints { make in
|
||
make.top.leading.equalToSuperview()
|
||
make.width.equalToSuperview().multipliedBy(0.5)
|
||
make.height.equalToSuperview().multipliedBy(0.5)
|
||
}
|
||
topRight.snp.makeConstraints { make in
|
||
make.top.trailing.equalToSuperview()
|
||
make.width.height.equalTo(topLeft)
|
||
}
|
||
bottomLeft.snp.makeConstraints { make in
|
||
make.bottom.leading.equalToSuperview()
|
||
make.width.height.equalTo(topLeft)
|
||
}
|
||
bottomRight.snp.makeConstraints { make in
|
||
make.bottom.trailing.equalToSuperview()
|
||
make.width.height.equalTo(topLeft)
|
||
}
|
||
|
||
let horizontalDivider = UIView()
|
||
let verticalDivider = UIView()
|
||
[horizontalDivider, verticalDivider].forEach {
|
||
$0.backgroundColor = UIColor(hex: 0xE7EBF1)
|
||
grid.addSubview($0)
|
||
}
|
||
horizontalDivider.snp.makeConstraints { make in
|
||
make.leading.trailing.centerY.equalToSuperview()
|
||
make.height.equalTo(1)
|
||
}
|
||
verticalDivider.snp.makeConstraints { make in
|
||
make.top.bottom.centerX.equalToSuperview()
|
||
make.width.equalTo(1)
|
||
}
|
||
|
||
summaryCard.addSubview(summaryDateLabel)
|
||
summaryCard.addSubview(grid)
|
||
summaryDateLabel.snp.makeConstraints { make in
|
||
make.top.leading.trailing.equalToSuperview().inset(16)
|
||
make.height.equalTo(24)
|
||
}
|
||
grid.snp.makeConstraints { make in
|
||
make.top.equalTo(summaryDateLabel.snp.bottom).offset(8)
|
||
make.leading.trailing.equalToSuperview().inset(10)
|
||
make.bottom.equalToSuperview().inset(10)
|
||
}
|
||
summaryCard.snp.makeConstraints { make in make.height.equalTo(238) }
|
||
}
|
||
|
||
private func metric(title: String, value: UILabel, badge: UILabel? = nil) -> UIView {
|
||
let titleLabel = UILabel()
|
||
titleLabel.text = title
|
||
titleLabel.font = .systemFont(ofSize: 13)
|
||
titleLabel.textColor = UIColor(hex: 0x7B8494)
|
||
titleLabel.textAlignment = .center
|
||
|
||
value.font = .systemFont(ofSize: 22, weight: .bold)
|
||
value.textColor = UIColor(hex: 0x081739)
|
||
value.textAlignment = .center
|
||
value.adjustsFontSizeToFitWidth = true
|
||
value.minimumScaleFactor = 0.72
|
||
|
||
let stack = UIStackView(arrangedSubviews: [titleLabel, value])
|
||
stack.axis = .vertical
|
||
stack.alignment = .fill
|
||
stack.spacing = 8
|
||
if let badge {
|
||
badge.text = "未结清"
|
||
badge.font = .systemFont(ofSize: 12, weight: .medium)
|
||
badge.textColor = UIColor(hex: 0xFF7600)
|
||
badge.textAlignment = .center
|
||
badge.backgroundColor = UIColor(hex: 0xFFF0E2)
|
||
badge.layer.cornerRadius = 7
|
||
badge.clipsToBounds = true
|
||
badge.snp.makeConstraints { make in make.width.equalTo(72); make.height.equalTo(26) }
|
||
let badgeContainer = UIView()
|
||
badgeContainer.addSubview(badge)
|
||
badge.snp.makeConstraints { make in make.center.equalToSuperview() }
|
||
stack.addArrangedSubview(badgeContainer)
|
||
badgeContainer.snp.makeConstraints { make in make.height.equalTo(26) }
|
||
stack.spacing = 5
|
||
}
|
||
|
||
let container = UIView()
|
||
container.addSubview(stack)
|
||
stack.snp.makeConstraints { make in
|
||
make.center.equalToSuperview()
|
||
make.leading.trailing.equalToSuperview().inset(12)
|
||
}
|
||
return container
|
||
}
|
||
|
||
private func configureBottomBar() {
|
||
bottomContainer.backgroundColor = .white
|
||
bottomContainer.layer.shadowColor = UIColor(hex: 0x8090A8).cgColor
|
||
bottomContainer.layer.shadowOpacity = 0.12
|
||
bottomContainer.layer.shadowRadius = 12
|
||
bottomContainer.layer.shadowOffset = CGSize(width: 0, height: -3)
|
||
settlementButton.setTitle("当日暂无待补缴", for: .normal)
|
||
settlementButton.setTitleColor(.white, for: .normal)
|
||
settlementButton.titleLabel?.font = .systemFont(ofSize: 17, weight: .semibold)
|
||
settlementButton.layer.cornerRadius = 10
|
||
settlementButton.clipsToBounds = true
|
||
settlementButton.accessibilityIdentifier = "offlineCollection.settle"
|
||
settlementButton.addTarget(self, action: #selector(settlementTapped), for: .touchUpInside)
|
||
bottomContainer.addSubview(settlementButton)
|
||
settlementButton.snp.makeConstraints { make in
|
||
make.top.equalToSuperview().offset(14)
|
||
make.leading.trailing.equalToSuperview().inset(16)
|
||
make.height.equalTo(54)
|
||
make.bottom.equalTo(bottomContainer.safeAreaLayoutGuide).inset(14)
|
||
}
|
||
}
|
||
|
||
@MainActor private func applyState() {
|
||
let processing = viewModel.settlementState == .processing
|
||
setGlobalLoadingVisible(viewModel.isLoading || processing)
|
||
calendarView.apply(
|
||
selectedDate: viewModel.businessDate,
|
||
maximumDate: viewModel.serverToday,
|
||
pendingDates: viewModel.pendingDates
|
||
)
|
||
summaryDateLabel.text = formattedSummaryDate()
|
||
totalValue.text = OfflineCollectionMoney.display(viewModel.summary.totalAmountFen)
|
||
countValue.text = "\(viewModel.summary.totalCount)笔"
|
||
settledValue.text = OfflineCollectionMoney.display(viewModel.summary.settledAmountFen)
|
||
pendingValue.text = OfflineCollectionMoney.display(viewModel.summary.pendingAmountFen)
|
||
pendingValue.textColor = viewModel.summary.pendingCount > 0 ? UIColor(hex: 0xFF7600) : UIColor(hex: 0x081739)
|
||
pendingStatusLabel.isHidden = viewModel.summary.pendingCount == 0
|
||
|
||
if let error = viewModel.errorMessage, !viewModel.isLoading {
|
||
statusButton.isHidden = false
|
||
statusButton.isUserInteractionEnabled = true
|
||
statusButton.configuration?.title = "\(error) 点击重试"
|
||
UIAccessibility.post(notification: .announcement, argument: error)
|
||
} else {
|
||
statusButton.isHidden = true
|
||
}
|
||
|
||
settlementButton.isEnabled = viewModel.canSettle && !processing
|
||
if processing {
|
||
settlementButton.setTitle("补缴中", for: .normal)
|
||
} else if viewModel.summary.totalCount > 0 && viewModel.summary.pendingCount == 0 {
|
||
settlementButton.setTitle("本日已全部补缴", for: .normal)
|
||
} else if viewModel.summary.pendingCount == 0 {
|
||
settlementButton.setTitle("当日暂无待补缴", for: .normal)
|
||
} else {
|
||
settlementButton.setTitle(
|
||
"补缴本日全部 \(OfflineCollectionMoney.display(viewModel.summary.pendingAmountFen))",
|
||
for: .normal
|
||
)
|
||
}
|
||
settlementButton.alpha = viewModel.canSettle || processing ? 1 : 0.45
|
||
applySnapshot()
|
||
resizeHeader()
|
||
presentFeedbackIfNeeded()
|
||
}
|
||
|
||
private func formattedSummaryDate() -> String {
|
||
guard let date = OfflineCollectionDate.date(from: viewModel.businessDate) else { return viewModel.businessDate }
|
||
let calendar = OfflineCollectionDate.calendar
|
||
let text = "\(calendar.component(.month, from: date))月\(calendar.component(.day, from: date))日"
|
||
return viewModel.businessDate == viewModel.serverToday ? "\(text) · 今日" : text
|
||
}
|
||
|
||
private func setGlobalLoadingVisible(_ visible: Bool) {
|
||
guard visible != isShowingGlobalLoading else { return }
|
||
isShowingGlobalLoading = visible
|
||
visible ? showLoading() : hideLoading()
|
||
}
|
||
|
||
@MainActor private func applySnapshot() {
|
||
var snapshot = NSDiffableDataSourceSnapshot<Int, OfflineDailyItem>()
|
||
snapshot.appendSections([0])
|
||
if viewModel.records.isEmpty {
|
||
let text = viewModel.errorMessage == nil && !viewModel.isLoading ? "本日暂无线下收款记录" : ""
|
||
snapshot.appendItems([.empty(text)])
|
||
} else {
|
||
snapshot.appendItems(viewModel.records.map(OfflineDailyItem.record))
|
||
}
|
||
dataSource.apply(snapshot, animatingDifferences: false)
|
||
}
|
||
|
||
private func makeDataSource() -> UITableViewDiffableDataSource<Int, OfflineDailyItem> {
|
||
UITableViewDiffableDataSource(tableView: tableView) { [weak self] tableView, indexPath, item in
|
||
switch item {
|
||
case let .record(record):
|
||
let cell = tableView.dequeueReusableCell(
|
||
withIdentifier: OfflineCollectionRecordCell.reuseIdentifier,
|
||
for: indexPath
|
||
) as! OfflineCollectionRecordCell
|
||
let count = self?.viewModel.records.count ?? 1
|
||
let position: OfflineRecordPosition
|
||
if count == 1 { position = .single }
|
||
else if indexPath.row == 0 { position = .first }
|
||
else if indexPath.row == count - 1 { position = .last }
|
||
else { position = .middle }
|
||
cell.apply(record, position: position)
|
||
return cell
|
||
case let .empty(text):
|
||
let cell = tableView.dequeueReusableCell(
|
||
withIdentifier: OfflineCollectionEmptyCell.reuseIdentifier,
|
||
for: indexPath
|
||
) as! OfflineCollectionEmptyCell
|
||
cell.apply(text)
|
||
return cell
|
||
}
|
||
}
|
||
}
|
||
|
||
private func resizeHeader() {
|
||
guard tableView.bounds.width > 0 else { return }
|
||
headerContainer.frame.size.width = tableView.bounds.width
|
||
headerContainer.setNeedsLayout()
|
||
headerContainer.layoutIfNeeded()
|
||
let height = headerContainer.systemLayoutSizeFitting(
|
||
CGSize(width: tableView.bounds.width, height: UIView.layoutFittingCompressedSize.height),
|
||
withHorizontalFittingPriority: .required,
|
||
verticalFittingPriority: .fittingSizeLevel
|
||
).height
|
||
guard abs(headerContainer.frame.height - height) > 0.5 else { return }
|
||
headerContainer.frame.size.height = height
|
||
tableView.tableHeaderView = headerContainer
|
||
}
|
||
|
||
@MainActor private func presentFeedbackIfNeeded() {
|
||
guard !feedbackPresented else { return }
|
||
switch viewModel.settlementState {
|
||
case .idle, .processing:
|
||
return
|
||
case let .success(result):
|
||
feedbackPresented = true
|
||
let alert = UIAlertController(
|
||
title: "补缴成功",
|
||
message: "营业日:\(result.date)\n共结清 \(result.updatedCount) 笔",
|
||
preferredStyle: .alert
|
||
)
|
||
alert.addAction(UIAlertAction(title: "完成", style: .default) { [weak self] _ in self?.finishFeedback() })
|
||
present(alert, animated: true)
|
||
case let .failed(message, canRetry):
|
||
feedbackPresented = true
|
||
let alert = UIAlertController(title: "补缴失败", message: message, preferredStyle: .alert)
|
||
alert.addAction(UIAlertAction(title: "暂不处理", style: .cancel) { [weak self] _ in
|
||
self?.viewModel.cancelPreparedSettlement()
|
||
self?.finishFeedback()
|
||
})
|
||
if canRetry {
|
||
alert.addAction(UIAlertAction(title: "重新补缴", style: .default) { [weak self] _ in
|
||
guard let self else { return }
|
||
self.feedbackPresented = false
|
||
Task { await self.viewModel.confirmSettlement() }
|
||
})
|
||
}
|
||
present(alert, animated: true)
|
||
}
|
||
}
|
||
|
||
private func finishFeedback() {
|
||
feedbackPresented = false
|
||
viewModel.clearSettlementFeedback()
|
||
}
|
||
|
||
@objc private func retryTapped() {
|
||
Task { await viewModel.refresh() }
|
||
}
|
||
|
||
@objc private func settlementTapped() {
|
||
do {
|
||
let request = try viewModel.prepareSettlement()
|
||
let message = "营业日:\(request.businessDate)\n待补缴记录:\(request.pendingCount) 笔\n本次补缴金额:\(OfflineCollectionMoney.display(request.amountFen))"
|
||
let alert = UIAlertController(title: "确认补缴", message: message, preferredStyle: .alert)
|
||
alert.addAction(UIAlertAction(title: "取消", style: .cancel) { [weak self] _ in
|
||
self?.viewModel.cancelPreparedSettlement()
|
||
})
|
||
alert.addAction(UIAlertAction(title: "确认补缴", style: .default) { [weak self] _ in
|
||
guard let self else { return }
|
||
Task { await self.viewModel.confirmSettlement() }
|
||
})
|
||
present(alert, animated: true)
|
||
} catch {
|
||
showToast(error.localizedDescription)
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 日清页的一笔收款记录行,纯展示支付方式、编号、金额、状态与时间。
|
||
private final class OfflineCollectionRecordCell: UITableViewCell {
|
||
static let reuseIdentifier = "OfflineCollectionRecordCell"
|
||
|
||
private let card = UIView()
|
||
private let iconView = UIImageView()
|
||
private let titleLabel = UILabel()
|
||
private let idLabel = UILabel()
|
||
private let amountLabel = UILabel()
|
||
private let statusLabel = UILabel()
|
||
private let timeLabel = UILabel()
|
||
private let separator = UIView()
|
||
|
||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||
selectionStyle = .none
|
||
backgroundColor = .clear
|
||
contentView.backgroundColor = .clear
|
||
|
||
card.backgroundColor = .white
|
||
iconView.contentMode = .scaleAspectFit
|
||
titleLabel.font = .systemFont(ofSize: 15, weight: .semibold)
|
||
titleLabel.textColor = UIColor(hex: 0x081739)
|
||
|
||
idLabel.font = .systemFont(ofSize: 9.5, weight: .medium)
|
||
idLabel.textColor = UIColor(hex: 0x6F7B8F)
|
||
idLabel.backgroundColor = UIColor(hex: 0xF0F2F6)
|
||
idLabel.layer.cornerRadius = 5
|
||
idLabel.clipsToBounds = true
|
||
idLabel.textAlignment = .center
|
||
idLabel.adjustsFontSizeToFitWidth = true
|
||
idLabel.minimumScaleFactor = 0.7
|
||
|
||
amountLabel.font = .systemFont(ofSize: 16, weight: .bold)
|
||
amountLabel.textColor = UIColor(hex: 0x081739)
|
||
amountLabel.textAlignment = .right
|
||
amountLabel.adjustsFontSizeToFitWidth = true
|
||
amountLabel.minimumScaleFactor = 0.72
|
||
|
||
statusLabel.font = .systemFont(ofSize: 11, weight: .medium)
|
||
statusLabel.textAlignment = .center
|
||
statusLabel.layer.cornerRadius = 7
|
||
statusLabel.clipsToBounds = true
|
||
|
||
timeLabel.font = .systemFont(ofSize: 12)
|
||
timeLabel.textColor = UIColor(hex: 0x7B8494)
|
||
timeLabel.textAlignment = .right
|
||
separator.backgroundColor = UIColor(hex: 0xE7EBF1)
|
||
|
||
let idContainer = UIView()
|
||
idContainer.addSubview(idLabel)
|
||
idLabel.snp.makeConstraints { make in
|
||
make.leading.top.bottom.equalToSuperview()
|
||
make.trailing.lessThanOrEqualToSuperview()
|
||
}
|
||
idContainer.snp.makeConstraints { make in make.height.equalTo(20) }
|
||
let leftStack = UIStackView(arrangedSubviews: [titleLabel, idContainer])
|
||
leftStack.axis = .vertical
|
||
leftStack.spacing = 4
|
||
|
||
card.addSubview(iconView)
|
||
card.addSubview(leftStack)
|
||
card.addSubview(amountLabel)
|
||
card.addSubview(statusLabel)
|
||
card.addSubview(timeLabel)
|
||
card.addSubview(separator)
|
||
contentView.addSubview(card)
|
||
|
||
card.snp.makeConstraints { make in
|
||
make.top.bottom.equalToSuperview()
|
||
make.leading.trailing.equalToSuperview().inset(16)
|
||
make.height.greaterThanOrEqualTo(78)
|
||
}
|
||
iconView.snp.makeConstraints { make in
|
||
make.leading.equalToSuperview().offset(12)
|
||
make.centerY.equalToSuperview()
|
||
make.width.height.equalTo(38)
|
||
}
|
||
leftStack.snp.makeConstraints { make in
|
||
make.leading.equalTo(iconView.snp.trailing).offset(10)
|
||
make.centerY.equalToSuperview()
|
||
make.width.equalTo(106)
|
||
}
|
||
amountLabel.snp.makeConstraints { make in
|
||
make.leading.greaterThanOrEqualTo(leftStack.snp.trailing).offset(4)
|
||
make.centerY.equalToSuperview()
|
||
make.width.equalTo(68)
|
||
}
|
||
statusLabel.snp.makeConstraints { make in
|
||
make.leading.equalTo(amountLabel.snp.trailing).offset(4)
|
||
make.centerY.equalToSuperview()
|
||
make.width.equalTo(50)
|
||
make.height.equalTo(27)
|
||
}
|
||
timeLabel.snp.makeConstraints { make in
|
||
make.leading.equalTo(statusLabel.snp.trailing).offset(2)
|
||
make.trailing.equalToSuperview().inset(10)
|
||
make.centerY.equalToSuperview()
|
||
make.width.greaterThanOrEqualTo(34)
|
||
}
|
||
separator.snp.makeConstraints { make in
|
||
make.leading.equalToSuperview().offset(12)
|
||
make.trailing.equalToSuperview().inset(8)
|
||
make.bottom.equalToSuperview()
|
||
make.height.equalTo(1 / UIScreen.main.scale)
|
||
}
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||
|
||
func apply(_ record: OfflineCollectionRecord, position: OfflineRecordPosition) {
|
||
iconView.image = UIImage(named: record.paymentMethod.assetName)
|
||
titleLabel.text = record.paymentMethod == .wechat ? "微信收款" : record.paymentMethod.displayName
|
||
idLabel.text = "编号 \(record.collectNo)"
|
||
amountLabel.text = OfflineCollectionMoney.display(record.amountFen)
|
||
let settled = record.status == .settled
|
||
statusLabel.text = settled ? "已补缴" : "未补缴"
|
||
statusLabel.textColor = settled ? UIColor(hex: 0x16A34A) : UIColor(hex: 0xFF7600)
|
||
statusLabel.backgroundColor = settled ? UIColor(hex: 0xEAF8EF) : UIColor(hex: 0xFFF0E2)
|
||
timeLabel.text = record.timeText
|
||
separator.isHidden = position == .single || position == .last
|
||
|
||
card.layer.cornerRadius = 12
|
||
switch position {
|
||
case .single:
|
||
card.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
||
case .first:
|
||
card.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||
case .middle:
|
||
card.layer.maskedCorners = []
|
||
case .last:
|
||
card.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
||
}
|
||
accessibilityLabel = "\(titleLabel.text ?? ""),编号 \(record.collectNo),\(amountLabel.text ?? ""),\(statusLabel.text ?? ""),\(record.timeText)"
|
||
}
|
||
}
|
||
|
||
/// 日清页空数据占位。
|
||
private final class OfflineCollectionEmptyCell: UITableViewCell {
|
||
static let reuseIdentifier = "OfflineCollectionEmptyCell"
|
||
private let label = UILabel()
|
||
|
||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||
selectionStyle = .none
|
||
backgroundColor = .clear
|
||
label.font = .systemFont(ofSize: 14)
|
||
label.textColor = UIColor(hex: 0x7B8494)
|
||
label.textAlignment = .center
|
||
contentView.addSubview(label)
|
||
label.snp.makeConstraints { make in make.edges.equalToSuperview().inset(36) }
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||
|
||
func apply(_ text: String) {
|
||
label.text = text
|
||
}
|
||
}
|