feat: 添加获客员分成记录并重构列表
This commit is contained in:
@ -0,0 +1,284 @@
|
||||
//
|
||||
// CommissionRateLogViewController.swift
|
||||
// suixinkan
|
||||
//
|
||||
|
||||
import SnapKit
|
||||
import UIKit
|
||||
|
||||
/// 获客员分成比例修改记录页,以时间轴形式展示历史变更。
|
||||
final class CommissionRateLogViewController: BaseViewController {
|
||||
private let viewModel: CommissionRateLogViewModel
|
||||
private let orderAPI: OrderAPI
|
||||
private let navigationTitle: String
|
||||
|
||||
private let tableView = UITableView(frame: .zero, style: .plain)
|
||||
private let emptyLabel = UILabel()
|
||||
private let loadingIndicator = UIActivityIndicatorView(style: .medium)
|
||||
private let footerLabel = UILabel()
|
||||
private var dataSource: UITableViewDiffableDataSource<Int, CommissionRateLogEntity>!
|
||||
|
||||
init(saleUserId: Int, acquirerName: String, orderAPI: OrderAPI? = nil) {
|
||||
viewModel = CommissionRateLogViewModel(saleUserId: saleUserId)
|
||||
let trimmedName = acquirerName.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
navigationTitle = trimmedName.isEmpty ? "修改记录" : "给\(trimmedName)的分成记录"
|
||||
self.orderAPI = orderAPI ?? NetworkServices.shared.orderAPI
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
Task { await viewModel.refresh(api: orderAPI) }
|
||||
}
|
||||
|
||||
override func setupNavigationBar() {
|
||||
title = navigationTitle
|
||||
}
|
||||
|
||||
override func setupUI() {
|
||||
view.backgroundColor = AppColor.pageBackground
|
||||
|
||||
tableView.backgroundColor = AppColor.pageBackground
|
||||
tableView.separatorStyle = .none
|
||||
tableView.rowHeight = UITableView.automaticDimension
|
||||
tableView.estimatedRowHeight = 132
|
||||
tableView.delegate = self
|
||||
tableView.contentInset.top = AppSpacing.sm
|
||||
tableView.register(
|
||||
CommissionRateLogTimelineCell.self,
|
||||
forCellReuseIdentifier: CommissionRateLogTimelineCell.reuseIdentifier
|
||||
)
|
||||
tableView.refreshControl = UIRefreshControl()
|
||||
tableView.refreshControl?.addTarget(self, action: #selector(refreshPulled), for: .valueChanged)
|
||||
|
||||
dataSource = UITableViewDiffableDataSource<Int, CommissionRateLogEntity>(
|
||||
tableView: tableView
|
||||
) { tableView, indexPath, item in
|
||||
guard let cell = tableView.dequeueReusableCell(
|
||||
withIdentifier: CommissionRateLogTimelineCell.reuseIdentifier,
|
||||
for: indexPath
|
||||
) as? CommissionRateLogTimelineCell else {
|
||||
return UITableViewCell()
|
||||
}
|
||||
cell.configure(with: item, isLatest: indexPath.item == 0)
|
||||
return cell
|
||||
}
|
||||
|
||||
emptyLabel.text = "暂无修改记录"
|
||||
emptyLabel.font = .systemFont(ofSize: 14)
|
||||
emptyLabel.textColor = AppColor.textTertiary
|
||||
emptyLabel.textAlignment = .center
|
||||
emptyLabel.isHidden = true
|
||||
|
||||
loadingIndicator.color = AppColor.primary
|
||||
|
||||
footerLabel.font = .systemFont(ofSize: 13)
|
||||
footerLabel.textColor = AppColor.textTertiary
|
||||
footerLabel.textAlignment = .center
|
||||
|
||||
view.addSubview(tableView)
|
||||
view.addSubview(emptyLabel)
|
||||
view.addSubview(loadingIndicator)
|
||||
}
|
||||
|
||||
override func setupConstraints() {
|
||||
tableView.snp.makeConstraints { make in
|
||||
make.edges.equalTo(view.safeAreaLayoutGuide)
|
||||
}
|
||||
emptyLabel.snp.makeConstraints { make in
|
||||
make.center.equalTo(tableView)
|
||||
}
|
||||
loadingIndicator.snp.makeConstraints { make in
|
||||
make.center.equalTo(tableView)
|
||||
}
|
||||
}
|
||||
|
||||
override func bindActions() {
|
||||
viewModel.onStateChange = { [weak self] in
|
||||
Task { @MainActor in self?.applyState() }
|
||||
}
|
||||
viewModel.onShowMessage = { [weak self] message in
|
||||
Task { @MainActor in self?.showToast(message) }
|
||||
}
|
||||
}
|
||||
|
||||
private func applyState() {
|
||||
if !viewModel.isRefreshing {
|
||||
tableView.refreshControl?.endRefreshing()
|
||||
}
|
||||
if viewModel.initialLoading && viewModel.items.isEmpty {
|
||||
loadingIndicator.startAnimating()
|
||||
} else {
|
||||
loadingIndicator.stopAnimating()
|
||||
}
|
||||
emptyLabel.isHidden = viewModel.initialLoading || viewModel.isRefreshing || !viewModel.items.isEmpty
|
||||
|
||||
var snapshot = NSDiffableDataSourceSnapshot<Int, CommissionRateLogEntity>()
|
||||
snapshot.appendSections([0])
|
||||
snapshot.appendItems(viewModel.items)
|
||||
dataSource.apply(snapshot, animatingDifferences: true)
|
||||
|
||||
guard !viewModel.items.isEmpty else {
|
||||
tableView.tableFooterView = UIView()
|
||||
return
|
||||
}
|
||||
footerLabel.text = viewModel.isLoadingMore ? "加载中…" : (viewModel.canLoadMore ? "" : "没有更多")
|
||||
footerLabel.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: 52)
|
||||
tableView.tableFooterView = footerLabel
|
||||
}
|
||||
|
||||
@objc private func refreshPulled() {
|
||||
Task { await viewModel.refresh(api: orderAPI) }
|
||||
}
|
||||
}
|
||||
|
||||
extension CommissionRateLogViewController: UITableViewDelegate {
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
guard !viewModel.items.isEmpty,
|
||||
scrollView.contentOffset.y > scrollView.contentSize.height - scrollView.bounds.height - 180 else {
|
||||
return
|
||||
}
|
||||
Task { await viewModel.loadMore(api: orderAPI) }
|
||||
}
|
||||
}
|
||||
|
||||
/// 分成比例修改记录的时间轴 Cell。
|
||||
final class CommissionRateLogTimelineCell: UITableViewCell {
|
||||
static let reuseIdentifier = "CommissionRateLogTimelineCell"
|
||||
|
||||
private let railView = UIView()
|
||||
private let dotView = UIView()
|
||||
private let cardView = UIView()
|
||||
private let metadataLabel = UILabel()
|
||||
private let latestBadgeLabel = UILabel()
|
||||
private let beforeRateLabel = UILabel()
|
||||
private let arrowImageView = UIImageView(image: UIImage(systemName: "arrow.right"))
|
||||
private let afterRateLabel = UILabel()
|
||||
private let remarkLabel = UILabel()
|
||||
private let contentStack = UIStackView()
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
selectionStyle = .none
|
||||
backgroundColor = AppColor.pageBackground
|
||||
contentView.backgroundColor = AppColor.pageBackground
|
||||
|
||||
railView.backgroundColor = AppColor.primary.withAlphaComponent(0.16)
|
||||
dotView.backgroundColor = AppColor.primary
|
||||
dotView.layer.cornerRadius = 4
|
||||
|
||||
cardView.backgroundColor = .white
|
||||
cardView.layer.cornerRadius = AppRadius.md
|
||||
cardView.layer.shadowColor = UIColor.black.cgColor
|
||||
cardView.layer.shadowOpacity = 0.04
|
||||
cardView.layer.shadowOffset = CGSize(width: 0, height: 1)
|
||||
cardView.layer.shadowRadius = 2
|
||||
|
||||
metadataLabel.font = .systemFont(ofSize: 12)
|
||||
metadataLabel.textColor = AppColor.textSecondary
|
||||
metadataLabel.numberOfLines = 2
|
||||
|
||||
latestBadgeLabel.text = "最新"
|
||||
latestBadgeLabel.font = .systemFont(ofSize: 11, weight: .semibold)
|
||||
latestBadgeLabel.textColor = AppColor.primary
|
||||
latestBadgeLabel.textAlignment = .center
|
||||
latestBadgeLabel.backgroundColor = AppColor.primary.withAlphaComponent(0.10)
|
||||
latestBadgeLabel.layer.cornerRadius = 9
|
||||
latestBadgeLabel.clipsToBounds = true
|
||||
latestBadgeLabel.isHidden = true
|
||||
latestBadgeLabel.setContentHuggingPriority(.required, for: .horizontal)
|
||||
latestBadgeLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||
latestBadgeLabel.snp.makeConstraints { make in
|
||||
make.width.equalTo(38)
|
||||
make.height.equalTo(18)
|
||||
}
|
||||
|
||||
let headerStack = UIStackView(arrangedSubviews: [metadataLabel, latestBadgeLabel])
|
||||
headerStack.axis = .horizontal
|
||||
headerStack.alignment = .top
|
||||
headerStack.spacing = AppSpacing.sm
|
||||
|
||||
[beforeRateLabel, afterRateLabel].forEach { label in
|
||||
label.font = .systemFont(ofSize: 17, weight: .semibold)
|
||||
label.textColor = AppColor.textPrimary
|
||||
label.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||
}
|
||||
afterRateLabel.textColor = AppColor.primary
|
||||
arrowImageView.tintColor = AppColor.textTertiary
|
||||
arrowImageView.contentMode = .scaleAspectFit
|
||||
|
||||
let rateStack = UIStackView(arrangedSubviews: [beforeRateLabel, arrowImageView, afterRateLabel, UIView()])
|
||||
rateStack.axis = .horizontal
|
||||
rateStack.alignment = .center
|
||||
rateStack.spacing = AppSpacing.sm
|
||||
arrowImageView.snp.makeConstraints { make in
|
||||
make.size.equalTo(16)
|
||||
}
|
||||
|
||||
remarkLabel.font = .systemFont(ofSize: 13)
|
||||
remarkLabel.textColor = AppColor.textSecondary
|
||||
remarkLabel.numberOfLines = 0
|
||||
|
||||
contentStack.axis = .vertical
|
||||
contentStack.spacing = AppSpacing.sm
|
||||
contentStack.addArrangedSubview(headerStack)
|
||||
contentStack.addArrangedSubview(rateStack)
|
||||
contentStack.addArrangedSubview(remarkLabel)
|
||||
|
||||
contentView.addSubview(railView)
|
||||
contentView.addSubview(dotView)
|
||||
contentView.addSubview(cardView)
|
||||
cardView.addSubview(contentStack)
|
||||
|
||||
railView.snp.makeConstraints { make in
|
||||
make.leading.equalToSuperview().offset(24)
|
||||
make.top.bottom.equalToSuperview()
|
||||
make.width.equalTo(1.5)
|
||||
}
|
||||
dotView.snp.makeConstraints { make in
|
||||
make.centerX.equalTo(railView)
|
||||
make.top.equalToSuperview().offset(20)
|
||||
make.size.equalTo(8)
|
||||
}
|
||||
cardView.snp.makeConstraints { make in
|
||||
make.leading.equalTo(railView.snp.trailing).offset(16)
|
||||
make.trailing.equalToSuperview().inset(14)
|
||||
make.top.equalToSuperview().offset(4)
|
||||
make.bottom.equalToSuperview().inset(8)
|
||||
}
|
||||
contentStack.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview().inset(15)
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
/// 绑定比例变化、时间及可选备注。
|
||||
func configure(with item: CommissionRateLogEntity, isLatest: Bool) {
|
||||
metadataLabel.text = item.metadataLine
|
||||
latestBadgeLabel.isHidden = !isLatest
|
||||
beforeRateLabel.text = item.displayBeforeRate
|
||||
afterRateLabel.text = item.displayAfterRate
|
||||
if let remark = item.displayRemark {
|
||||
remarkLabel.text = "备注:\(remark)"
|
||||
remarkLabel.isHidden = false
|
||||
} else {
|
||||
remarkLabel.text = nil
|
||||
remarkLabel.isHidden = true
|
||||
}
|
||||
accessibilityLabel = [
|
||||
isLatest ? "最新记录" : nil,
|
||||
item.metadataLine,
|
||||
"\(item.displayBeforeRate)修改为\(item.displayAfterRate)",
|
||||
item.displayRemark.map { "备注\($0)" },
|
||||
].compactMap { $0 }.joined(separator: ",")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user