605 lines
26 KiB
Swift
605 lines
26 KiB
Swift
//
|
||
// OrdersViewController.swift
|
||
// suixinkan
|
||
//
|
||
|
||
import SnapKit
|
||
import UIKit
|
||
|
||
/// 订单 Tab 根页面,展示订单管理和核销订单两个子入口及对应列表。
|
||
final class OrdersViewController: UIViewController {
|
||
|
||
private let viewModel = OrdersViewModel()
|
||
private var manualOrderNumber = ""
|
||
private var scanHintMessage: String?
|
||
|
||
private lazy var tableView: UITableView = {
|
||
let table = UITableView(frame: .zero, style: .grouped)
|
||
table.backgroundColor = AppDesignUIKit.pageBackground
|
||
table.separatorStyle = .none
|
||
table.dataSource = self
|
||
table.delegate = self
|
||
table.register(OrderEntityCell.self, forCellReuseIdentifier: OrderEntityCell.reuseID)
|
||
table.register(WriteOffOrderCell.self, forCellReuseIdentifier: WriteOffOrderCell.reuseID)
|
||
table.register(OrdersHeaderCell.self, forCellReuseIdentifier: OrdersHeaderCell.reuseID)
|
||
table.register(OrdersFilterCell.self, forCellReuseIdentifier: OrdersFilterCell.reuseID)
|
||
table.register(OrdersWriteOffActionCell.self, forCellReuseIdentifier: OrdersWriteOffActionCell.reuseID)
|
||
return table
|
||
}()
|
||
|
||
private lazy var refreshControl = UIRefreshControl()
|
||
|
||
override func viewDidLoad() {
|
||
super.viewDidLoad()
|
||
title = "订单"
|
||
view.backgroundColor = AppDesignUIKit.pageBackground
|
||
|
||
view.addSubview(tableView)
|
||
tableView.snp.makeConstraints { make in
|
||
make.edges.equalToSuperview()
|
||
}
|
||
refreshControl.addTarget(self, action: #selector(refreshPulled), for: .valueChanged)
|
||
tableView.refreshControl = refreshControl
|
||
|
||
viewModel.onChange = { [weak self] in
|
||
self?.tableView.reloadData()
|
||
}
|
||
|
||
appServices.appRouter.onChange = { [weak self] in
|
||
guard let self else { return }
|
||
if self.viewModel.selectedEntry != self.appServices.appRouter.selectedOrdersEntry {
|
||
self.viewModel.selectedEntry = self.appServices.appRouter.selectedOrdersEntry
|
||
Task { await self.reload(showLoading: true) }
|
||
}
|
||
Task { await self.consumePendingScanCodeIfNeeded() }
|
||
}
|
||
appServices.accountContext.onChange = { [weak self] in
|
||
Task { await self?.reload(showLoading: true) }
|
||
}
|
||
|
||
viewModel.selectedEntry = appServices.appRouter.selectedOrdersEntry
|
||
Task {
|
||
await reload(showLoading: true)
|
||
await consumePendingScanCodeIfNeeded()
|
||
}
|
||
}
|
||
|
||
@objc private func refreshPulled() {
|
||
Task {
|
||
await reload(showLoading: false)
|
||
refreshControl.endRefreshing()
|
||
}
|
||
}
|
||
|
||
private var currentScenicId: Int? { appServices.accountContext.currentScenic?.id }
|
||
private var currentStoreId: Int? { appServices.accountContext.currentStore?.id }
|
||
private var currentRoleId: Int? { appServices.permissionContext.currentRole?.id }
|
||
|
||
private func reload(showLoading: Bool) async {
|
||
do {
|
||
try await appServices.globalLoading.withOptionalLoading(showLoading, message: "加载订单...") {
|
||
try await self.viewModel.reload(
|
||
api: self.appServices.ordersAPI,
|
||
scenicId: self.currentScenicId,
|
||
storeId: self.currentStoreId,
|
||
roleId: self.currentRoleId,
|
||
showLoading: false
|
||
)
|
||
}
|
||
} catch {
|
||
showToast(error.localizedDescription)
|
||
}
|
||
}
|
||
|
||
private func loadMore() async {
|
||
do {
|
||
if viewModel.selectedEntry == .storeOrders {
|
||
try await viewModel.loadMoreStoreOrders(
|
||
api: appServices.ordersAPI,
|
||
scenicId: currentScenicId,
|
||
roleId: currentRoleId
|
||
)
|
||
} else {
|
||
try await viewModel.loadMoreWriteOffOrders(
|
||
api: appServices.ordersAPI,
|
||
scenicId: currentScenicId,
|
||
storeId: currentStoreId
|
||
)
|
||
}
|
||
} catch {
|
||
showToast(error.localizedDescription)
|
||
}
|
||
}
|
||
|
||
private func switchEntry(_ entry: OrdersEntry) {
|
||
guard viewModel.selectedEntry != entry else { return }
|
||
viewModel.selectedEntry = entry
|
||
appServices.appRouter.selectedOrdersEntry = entry
|
||
Task { await reload(showLoading: true) }
|
||
}
|
||
|
||
private func verify(orderNumber: String) async {
|
||
guard let scenicId = currentScenicId else { return }
|
||
do {
|
||
try await appServices.globalLoading.withLoading(message: "核销中...") {
|
||
try await self.viewModel.verify(
|
||
api: self.appServices.ordersAPI,
|
||
scenicId: scenicId,
|
||
storeId: self.currentStoreId,
|
||
orderNumber: orderNumber
|
||
)
|
||
}
|
||
showToast("核销成功")
|
||
} catch {
|
||
showToast(error.localizedDescription)
|
||
}
|
||
}
|
||
|
||
private func consumePendingScanCodeIfNeeded() async {
|
||
guard viewModel.selectedEntry == .verificationOrders,
|
||
let code = appServices.appRouter.consumePendingOrderScanCode() else { return }
|
||
handleScanResult(code)
|
||
}
|
||
|
||
private func handleScanResult(_ rawCode: String) {
|
||
guard let parsed = viewModel.matchedWriteOffOrder(for: rawCode) else {
|
||
showToast("未识别到有效订单号")
|
||
return
|
||
}
|
||
if parsed.matched != nil {
|
||
confirmVerify(orderNumber: parsed.orderNumber)
|
||
} else {
|
||
scanHintMessage = "扫码成功,当前列表未找到该订单"
|
||
tableView.reloadData()
|
||
}
|
||
}
|
||
|
||
private func confirmVerify(orderNumber: String) {
|
||
let alert = UIAlertController(title: "确认核销该订单?", message: orderNumber, preferredStyle: .alert)
|
||
alert.addAction(UIAlertAction(title: "取消", style: .cancel))
|
||
alert.addAction(UIAlertAction(title: "确认核销", style: .default) { [weak self] _ in
|
||
Task { await self?.verify(orderNumber: orderNumber) }
|
||
})
|
||
present(alert, animated: true)
|
||
}
|
||
|
||
private func presentScanner() {
|
||
let scanner = OrderCodeScannerViewController()
|
||
scanner.onScanResult = { [weak self, weak scanner] result in
|
||
scanner?.dismiss(animated: true)
|
||
guard let self else { return }
|
||
switch result {
|
||
case .success(let code):
|
||
self.handleScanResult(code)
|
||
case .failure(let error):
|
||
self.showToast(error.localizedDescription)
|
||
}
|
||
}
|
||
let nav = UINavigationController(rootViewController: scanner)
|
||
nav.modalPresentationStyle = .fullScreen
|
||
present(nav, animated: true)
|
||
}
|
||
|
||
private func presentDateFilter() {
|
||
let alert = UIAlertController(title: "时间筛选", message: "选择开始和结束日期", preferredStyle: .alert)
|
||
alert.addAction(UIAlertAction(title: "清除筛选", style: .destructive) { [weak self] _ in
|
||
self?.viewModel.filterStartDate = nil
|
||
self?.viewModel.filterEndDate = nil
|
||
Task { await self?.reload(showLoading: true) }
|
||
})
|
||
alert.addAction(UIAlertAction(title: "近7天", style: .default) { [weak self] _ in
|
||
let end = Date()
|
||
let start = Calendar.current.date(byAdding: .day, value: -6, to: end) ?? end
|
||
self?.viewModel.filterStartDate = start
|
||
self?.viewModel.filterEndDate = end
|
||
Task { await self?.reload(showLoading: true) }
|
||
})
|
||
alert.addAction(UIAlertAction(title: "取消", style: .cancel))
|
||
present(alert, animated: true)
|
||
}
|
||
|
||
private func presentStatusFilter() {
|
||
let sheet = UIAlertController(title: "订单状态", message: nil, preferredStyle: .actionSheet)
|
||
for filter in OrderFilters.statusFilters {
|
||
sheet.addAction(UIAlertAction(title: filter.title, style: .default) { [weak self] _ in
|
||
self?.viewModel.selectedStatus = filter.id
|
||
Task { await self?.reload(showLoading: true) }
|
||
})
|
||
}
|
||
sheet.addAction(UIAlertAction(title: "取消", style: .cancel))
|
||
present(sheet, animated: true)
|
||
}
|
||
}
|
||
|
||
extension OrdersViewController: UITableViewDataSource, UITableViewDelegate {
|
||
func numberOfSections(in tableView: UITableView) -> Int {
|
||
guard currentScenicId != nil else { return 1 }
|
||
return viewModel.selectedEntry == .storeOrders ? 3 : 3
|
||
}
|
||
|
||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||
if section == 0 { return 1 }
|
||
if section == 1 { return viewModel.selectedEntry == .storeOrders ? 1 : 1 }
|
||
if currentScenicId == nil { return 1 }
|
||
if viewModel.selectedEntry == .storeOrders {
|
||
return max(viewModel.storeOrders.count, viewModel.loading && viewModel.storeOrders.isEmpty ? 0 : 1)
|
||
}
|
||
return max(viewModel.writeOffOrders.count, viewModel.loading && viewModel.writeOffOrders.isEmpty ? 0 : 1)
|
||
}
|
||
|
||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||
if indexPath.section == 0 {
|
||
let cell = tableView.dequeueReusableCell(withIdentifier: OrdersHeaderCell.reuseID, for: indexPath) as! OrdersHeaderCell
|
||
let services = appServices
|
||
cell.configure(
|
||
selectedEntry: viewModel.selectedEntry,
|
||
scenicName: services.accountContext.currentScenic?.name ?? "--",
|
||
storeTotal: viewModel.storeTotal,
|
||
writeOffTotal: viewModel.writeOffTotal,
|
||
onSelectEntry: { [weak self] entry in self?.switchEntry(entry) }
|
||
)
|
||
return cell
|
||
}
|
||
|
||
if currentScenicId == nil {
|
||
let cell = UITableViewCell()
|
||
cell.selectionStyle = .none
|
||
cell.backgroundColor = .clear
|
||
cell.contentView.subviews.forEach { $0.removeFromSuperview() }
|
||
let empty = makeEmptyStateView(title: "缺少经营上下文", message: "请先在首页选择景区后查看订单。", systemImage: "mountain.2")
|
||
cell.contentView.addSubview(empty)
|
||
empty.snp.makeConstraints { make in
|
||
make.edges.equalToSuperview()
|
||
make.height.equalTo(360)
|
||
}
|
||
return cell
|
||
}
|
||
|
||
if indexPath.section == 1 {
|
||
if viewModel.selectedEntry == .storeOrders {
|
||
let cell = tableView.dequeueReusableCell(withIdentifier: OrdersFilterCell.reuseID, for: indexPath) as! OrdersFilterCell
|
||
let statusTitle = OrderFilters.statusFilters.first(where: { $0.id == viewModel.selectedStatus })?.title ?? "全部"
|
||
cell.configure(
|
||
statusTitle: statusTitle,
|
||
phone: viewModel.searchPhone,
|
||
onStatus: { [weak self] in self?.presentStatusFilter() },
|
||
onDate: { [weak self] in self?.presentDateFilter() },
|
||
onPhoneChange: { [weak self] text in self?.viewModel.searchPhone = text },
|
||
onSearch: { [weak self] in Task { await self?.reload(showLoading: true) } }
|
||
)
|
||
return cell
|
||
}
|
||
let cell = tableView.dequeueReusableCell(withIdentifier: OrdersWriteOffActionCell.reuseID, for: indexPath) as! OrdersWriteOffActionCell
|
||
cell.configure(
|
||
manualOrderNumber: manualOrderNumber,
|
||
isVerifying: viewModel.isVerifying,
|
||
hint: scanHintMessage,
|
||
onScan: { [weak self] in self?.presentScanner() },
|
||
onManualChange: { [weak self] text in self?.manualOrderNumber = text },
|
||
onVerify: { [weak self] in
|
||
guard let self, !self.manualOrderNumber.isEmpty else { return }
|
||
self.confirmVerify(orderNumber: self.manualOrderNumber)
|
||
}
|
||
)
|
||
return cell
|
||
}
|
||
|
||
if viewModel.selectedEntry == .storeOrders {
|
||
if viewModel.storeOrders.isEmpty {
|
||
let cell = UITableViewCell()
|
||
cell.selectionStyle = .none
|
||
cell.backgroundColor = .clear
|
||
cell.contentView.subviews.forEach { $0.removeFromSuperview() }
|
||
let empty = makeEmptyStateView(title: "暂无订单", message: "可切换筛选条件或下拉刷新。", systemImage: "tray")
|
||
cell.contentView.addSubview(empty)
|
||
empty.snp.makeConstraints { make in make.edges.equalToSuperview(); make.height.equalTo(260) }
|
||
return cell
|
||
}
|
||
let item = viewModel.storeOrders[indexPath.row]
|
||
let cell = tableView.dequeueReusableCell(withIdentifier: OrderEntityCell.reuseID, for: indexPath) as! OrderEntityCell
|
||
cell.configure(item: item)
|
||
return cell
|
||
}
|
||
|
||
if viewModel.writeOffOrders.isEmpty {
|
||
let cell = UITableViewCell()
|
||
cell.selectionStyle = .none
|
||
cell.backgroundColor = .clear
|
||
cell.contentView.subviews.forEach { $0.removeFromSuperview() }
|
||
let empty = makeEmptyStateView(title: "暂无核销订单", message: "可下拉刷新或切换景区查看。", systemImage: "tray")
|
||
cell.contentView.addSubview(empty)
|
||
empty.snp.makeConstraints { make in make.edges.equalToSuperview(); make.height.equalTo(260) }
|
||
return cell
|
||
}
|
||
let item = viewModel.writeOffOrders[indexPath.row]
|
||
let cell = tableView.dequeueReusableCell(withIdentifier: WriteOffOrderCell.reuseID, for: indexPath) as! WriteOffOrderCell
|
||
cell.configure(item: item, isVerifying: viewModel.currentVerifyingOrderNumber == item.orderNumber)
|
||
return cell
|
||
}
|
||
|
||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||
tableView.deselectRow(at: indexPath, animated: true)
|
||
guard indexPath.section == 2 else { return }
|
||
if viewModel.selectedEntry == .storeOrders, indexPath.row < viewModel.storeOrders.count {
|
||
HomeMenuRouting.pushOrders(.storeDetail(viewModel.storeOrders[indexPath.row]), from: self)
|
||
} else if viewModel.selectedEntry == .verificationOrders, indexPath.row < viewModel.writeOffOrders.count {
|
||
HomeMenuRouting.pushOrders(.writeOffDetail(viewModel.writeOffOrders[indexPath.row]), from: self)
|
||
}
|
||
}
|
||
|
||
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||
guard indexPath.section == 2 else { return }
|
||
let isLast: Bool
|
||
if viewModel.selectedEntry == .storeOrders {
|
||
isLast = indexPath.row == viewModel.storeOrders.count - 1
|
||
} else {
|
||
isLast = indexPath.row == viewModel.writeOffOrders.count - 1
|
||
}
|
||
if isLast { Task { await loadMore() } }
|
||
}
|
||
|
||
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||
if indexPath.section == 0 { return 120 }
|
||
if indexPath.section == 1 { return viewModel.selectedEntry == .storeOrders ? 130 : 150 }
|
||
return UITableView.automaticDimension
|
||
}
|
||
}
|
||
|
||
// MARK: - Cells
|
||
|
||
private final class OrdersHeaderCell: UITableViewCell {
|
||
static let reuseID = "OrdersHeaderCell"
|
||
private var onSelectEntry: ((OrdersEntry) -> Void)?
|
||
|
||
private let storeButton = UIButton(type: .system)
|
||
private let verifyButton = UIButton(type: .system)
|
||
private let leftPill = UILabel()
|
||
private let rightPill = UILabel()
|
||
|
||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||
selectionStyle = .none
|
||
backgroundColor = .clear
|
||
let card = UIView()
|
||
card.backgroundColor = .white
|
||
card.layer.cornerRadius = 8
|
||
contentView.addSubview(card)
|
||
card.snp.makeConstraints { make in
|
||
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 4, left: 16, bottom: 4, right: 16))
|
||
}
|
||
|
||
let segmentBackground = UIView()
|
||
segmentBackground.backgroundColor = UIColor(hex: 0xF3F4F6)
|
||
segmentBackground.layer.cornerRadius = 8
|
||
card.addSubview(segmentBackground)
|
||
storeButton.addTarget(self, action: #selector(storeTapped), for: .touchUpInside)
|
||
verifyButton.addTarget(self, action: #selector(verifyTapped), for: .touchUpInside)
|
||
segmentBackground.addSubview(storeButton)
|
||
segmentBackground.addSubview(verifyButton)
|
||
|
||
[leftPill, rightPill].forEach {
|
||
$0.font = .systemFont(ofSize: AppMetrics.FontSize.caption)
|
||
$0.numberOfLines = 2
|
||
card.addSubview($0)
|
||
}
|
||
|
||
segmentBackground.snp.makeConstraints { make in
|
||
make.top.leading.trailing.equalToSuperview().inset(12)
|
||
make.height.equalTo(40)
|
||
}
|
||
storeButton.snp.makeConstraints { make in
|
||
make.leading.top.bottom.equalToSuperview().inset(2)
|
||
make.width.equalToSuperview().multipliedBy(0.5)
|
||
}
|
||
verifyButton.snp.makeConstraints { make in
|
||
make.trailing.top.bottom.equalToSuperview().inset(2)
|
||
make.width.equalToSuperview().multipliedBy(0.5)
|
||
}
|
||
leftPill.snp.makeConstraints { make in
|
||
make.leading.bottom.equalToSuperview().inset(12)
|
||
make.top.equalTo(segmentBackground.snp.bottom).offset(10)
|
||
make.width.equalToSuperview().multipliedBy(0.45)
|
||
}
|
||
rightPill.snp.makeConstraints { make in
|
||
make.trailing.bottom.equalToSuperview().inset(12)
|
||
make.top.equalTo(leftPill)
|
||
make.width.equalToSuperview().multipliedBy(0.45)
|
||
}
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) { fatalError() }
|
||
|
||
func configure(selectedEntry: OrdersEntry, scenicName: String, storeTotal: Int, writeOffTotal: Int, onSelectEntry: @escaping (OrdersEntry) -> Void) {
|
||
self.onSelectEntry = onSelectEntry
|
||
updateSegment(storeButton, title: "订单管理", selected: selectedEntry == .storeOrders)
|
||
updateSegment(verifyButton, title: "核销订单", selected: selectedEntry == .verificationOrders)
|
||
if selectedEntry == .storeOrders {
|
||
leftPill.text = "当前景区\n\(scenicName)"
|
||
rightPill.text = "订单总数\n\(storeTotal)"
|
||
} else {
|
||
leftPill.text = "核销订单\n\(writeOffTotal)"
|
||
rightPill.text = "当前景区\n\(scenicName)"
|
||
}
|
||
}
|
||
|
||
private func updateSegment(_ button: UIButton, title: String, selected: Bool) {
|
||
button.setTitle(title, for: .normal)
|
||
button.titleLabel?.font = .systemFont(ofSize: AppMetrics.FontSize.subheadline, weight: selected ? .semibold : .medium)
|
||
button.setTitleColor(selected ? AppDesignUIKit.primary : AppDesignUIKit.textSecondary, for: .normal)
|
||
button.backgroundColor = selected ? .white : .clear
|
||
button.layer.cornerRadius = 6
|
||
}
|
||
|
||
@objc private func storeTapped() { onSelectEntry?(.storeOrders) }
|
||
@objc private func verifyTapped() { onSelectEntry?(.verificationOrders) }
|
||
}
|
||
|
||
private final class OrdersFilterCell: UITableViewCell, UITextFieldDelegate {
|
||
static let reuseID = "OrdersFilterCell"
|
||
private let phoneField = UITextField()
|
||
private var onPhoneChange: ((String) -> Void)?
|
||
private var onSearch: (() -> Void)?
|
||
|
||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||
selectionStyle = .none
|
||
backgroundColor = .clear
|
||
let card = UIView()
|
||
card.backgroundColor = .white
|
||
card.layer.cornerRadius = 8
|
||
contentView.addSubview(card)
|
||
card.snp.makeConstraints { make in
|
||
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 4, left: 16, bottom: 4, right: 16))
|
||
}
|
||
phoneField.delegate = self
|
||
phoneField.keyboardType = .phonePad
|
||
phoneField.font = .systemFont(ofSize: AppMetrics.FontSize.subheadline)
|
||
phoneField.backgroundColor = UIColor(hex: 0xF4F4F4)
|
||
phoneField.layer.cornerRadius = 6
|
||
phoneField.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 12, height: 1))
|
||
phoneField.leftViewMode = .always
|
||
card.addSubview(phoneField)
|
||
phoneField.snp.makeConstraints { make in
|
||
make.leading.bottom.equalToSuperview().inset(12)
|
||
make.height.equalTo(40)
|
||
make.trailing.equalToSuperview().inset(100)
|
||
}
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) { fatalError() }
|
||
|
||
func configure(statusTitle: String, phone: String, onStatus: @escaping () -> Void, onDate: @escaping () -> Void, onPhoneChange: @escaping (String) -> Void, onSearch: @escaping () -> Void) {
|
||
phoneField.text = phone
|
||
self.onPhoneChange = onPhoneChange
|
||
self.onSearch = onSearch
|
||
}
|
||
|
||
func textFieldDidChangeSelection(_ textField: UITextField) {
|
||
onPhoneChange?(textField.text ?? "")
|
||
}
|
||
}
|
||
|
||
private final class OrdersWriteOffActionCell: UITableViewCell, UITextFieldDelegate {
|
||
static let reuseID = "OrdersWriteOffActionCell"
|
||
private let manualField = UITextField()
|
||
private var onManualChange: ((String) -> Void)?
|
||
private var onVerify: (() -> Void)?
|
||
|
||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||
selectionStyle = .none
|
||
backgroundColor = .clear
|
||
let card = UIView()
|
||
card.backgroundColor = .white
|
||
card.layer.cornerRadius = 8
|
||
contentView.addSubview(card)
|
||
card.snp.makeConstraints { make in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 4, left: 16, bottom: 4, right: 16)) }
|
||
|
||
manualField.delegate = self
|
||
manualField.placeholder = "手动输入订单号"
|
||
manualField.autocorrectionType = .no
|
||
manualField.autocapitalizationType = .none
|
||
manualField.backgroundColor = UIColor(hex: 0xF4F4F4)
|
||
manualField.layer.cornerRadius = 8
|
||
manualField.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 12, height: 1))
|
||
manualField.leftViewMode = .always
|
||
card.addSubview(manualField)
|
||
manualField.snp.makeConstraints { make in
|
||
make.leading.bottom.equalToSuperview().inset(12)
|
||
make.height.equalTo(42)
|
||
make.trailing.equalToSuperview().inset(90)
|
||
}
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) { fatalError() }
|
||
|
||
func configure(manualOrderNumber: String, isVerifying: Bool, hint: String?, onScan: @escaping () -> Void, onManualChange: @escaping (String) -> Void, onVerify: @escaping () -> Void) {
|
||
manualField.text = manualOrderNumber
|
||
self.onManualChange = onManualChange
|
||
self.onVerify = onVerify
|
||
}
|
||
|
||
func textFieldDidChangeSelection(_ textField: UITextField) {
|
||
onManualChange?(textField.text ?? "")
|
||
}
|
||
}
|
||
|
||
private final class OrderEntityCell: UITableViewCell {
|
||
static let reuseID = "OrderEntityCell"
|
||
private let titleLabel = UILabel()
|
||
private let statusLabel = UILabel()
|
||
private let amountLabel = UILabel()
|
||
private let phoneLabel = UILabel()
|
||
|
||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||
selectionStyle = .default
|
||
backgroundColor = .clear
|
||
let card = UIView()
|
||
card.backgroundColor = .white
|
||
card.layer.cornerRadius = 8
|
||
contentView.addSubview(card)
|
||
card.snp.makeConstraints { make in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 4, left: 16, bottom: 4, right: 16)) }
|
||
|
||
titleLabel.font = .systemFont(ofSize: AppMetrics.FontSize.subheadline, weight: .semibold)
|
||
statusLabel.font = .systemFont(ofSize: AppMetrics.FontSize.caption, weight: .medium)
|
||
amountLabel.font = .systemFont(ofSize: AppMetrics.FontSize.body, weight: .semibold)
|
||
amountLabel.textColor = AppDesignUIKit.primary
|
||
phoneLabel.font = .systemFont(ofSize: AppMetrics.FontSize.caption)
|
||
phoneLabel.textColor = AppDesignUIKit.textSecondary
|
||
|
||
let stack = UIStackView(arrangedSubviews: [titleLabel, phoneLabel, amountLabel])
|
||
stack.axis = .vertical
|
||
stack.spacing = 4
|
||
card.addSubview(stack)
|
||
card.addSubview(statusLabel)
|
||
stack.snp.makeConstraints { make in make.leading.top.bottom.equalToSuperview().inset(12) }
|
||
statusLabel.snp.makeConstraints { make in make.trailing.top.equalToSuperview().inset(12) }
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) { fatalError() }
|
||
|
||
func configure(item: OrderEntity) {
|
||
titleLabel.text = item.orderNumber
|
||
statusLabel.text = item.orderStatusName
|
||
amountLabel.text = "¥\(item.actualPayAmount.isEmpty ? item.orderAmount : item.actualPayAmount)"
|
||
phoneLabel.text = item.phone
|
||
}
|
||
}
|
||
|
||
private final class WriteOffOrderCell: UITableViewCell {
|
||
static let reuseID = "WriteOffOrderCell"
|
||
private let titleLabel = UILabel()
|
||
private let subtitleLabel = UILabel()
|
||
|
||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||
backgroundColor = .clear
|
||
let card = UIView()
|
||
card.backgroundColor = .white
|
||
card.layer.cornerRadius = 8
|
||
contentView.addSubview(card)
|
||
card.snp.makeConstraints { make in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 4, left: 16, bottom: 4, right: 16)) }
|
||
titleLabel.font = .systemFont(ofSize: AppMetrics.FontSize.subheadline, weight: .semibold)
|
||
subtitleLabel.font = .systemFont(ofSize: AppMetrics.FontSize.caption)
|
||
subtitleLabel.textColor = AppDesignUIKit.textSecondary
|
||
let stack = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel])
|
||
stack.axis = .vertical
|
||
stack.spacing = 4
|
||
card.addSubview(stack)
|
||
stack.snp.makeConstraints { make in make.edges.equalToSuperview().inset(12) }
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) { fatalError() }
|
||
|
||
func configure(item: WriteOffOrderItem, isVerifying: Bool) {
|
||
titleLabel.text = item.orderNumber
|
||
subtitleLabel.text = isVerifying ? "核销中..." : (item.projectName.isEmpty ? item.userPhone : item.projectName)
|
||
}
|
||
}
|