Initial commit: suixinkan_ios UIKit rewrite project.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -0,0 +1,360 @@
|
||||
//
|
||||
// DepositOrderViewControllers.swift
|
||||
// suixinkan
|
||||
//
|
||||
|
||||
import SnapKit
|
||||
import UIKit
|
||||
|
||||
/// 押金订单列表页,支持查询、分页、核销和退款。
|
||||
final class DepositOrderListViewController: UIViewController {
|
||||
|
||||
private let viewModel = DepositOrderListViewModel()
|
||||
private var orderNumberField = UITextField()
|
||||
|
||||
private lazy var tableView: UITableView = {
|
||||
let table = UITableView(frame: .zero, style: .insetGrouped)
|
||||
table.dataSource = self
|
||||
table.delegate = self
|
||||
table.register(DepositOrderCell.self, forCellReuseIdentifier: DepositOrderCell.reuseID)
|
||||
return table
|
||||
}()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
title = "押金订单"
|
||||
view.backgroundColor = AppDesignUIKit.pageBackground
|
||||
view.addSubview(tableView)
|
||||
tableView.snp.makeConstraints { make in make.edges.equalToSuperview() }
|
||||
|
||||
viewModel.onChange = { [weak self] in self?.tableView.reloadData() }
|
||||
setupHeader()
|
||||
Task { await reload(showLoading: true) }
|
||||
}
|
||||
|
||||
private func setupHeader() {
|
||||
let header = UIView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 120))
|
||||
orderNumberField.placeholder = "请输入押金订单号"
|
||||
orderNumberField.borderStyle = .roundedRect
|
||||
orderNumberField.autocorrectionType = .no
|
||||
orderNumberField.autocapitalizationType = .none
|
||||
|
||||
let detailButton = makePrimaryButton(title: "查看订单详情")
|
||||
detailButton.addTarget(self, action: #selector(openDetail), for: .touchUpInside)
|
||||
|
||||
header.addSubview(orderNumberField)
|
||||
header.addSubview(detailButton)
|
||||
orderNumberField.snp.makeConstraints { make in
|
||||
make.top.leading.trailing.equalToSuperview().inset(16)
|
||||
make.height.equalTo(44)
|
||||
}
|
||||
detailButton.snp.makeConstraints { make in
|
||||
make.top.equalTo(orderNumberField.snp.bottom).offset(12)
|
||||
make.leading.trailing.equalToSuperview().inset(16)
|
||||
}
|
||||
tableView.tableHeaderView = header
|
||||
}
|
||||
|
||||
@objc private func openDetail() {
|
||||
let text = orderNumberField.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
guard !text.isEmpty else { return }
|
||||
HomeMenuRouting.pushOrders(.depositDetail(orderNumber: text), from: self)
|
||||
}
|
||||
|
||||
private func reload(showLoading: Bool) async {
|
||||
if showLoading {
|
||||
await appServices.globalLoading.withLoading {
|
||||
await viewModel.reload(api: appServices.ordersAPI, scenicId: appServices.accountContext.currentScenic?.id, reset: true)
|
||||
}
|
||||
} else {
|
||||
await viewModel.reload(api: appServices.ordersAPI, scenicId: appServices.accountContext.currentScenic?.id, reset: true)
|
||||
}
|
||||
if let message = viewModel.errorMessage { showToast(message) }
|
||||
}
|
||||
|
||||
private func writeOff(_ item: DepositOrderListItem) {
|
||||
let alert = UIAlertController(title: "确认核销该押金订单?", message: item.orderNumber, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "取消", style: .cancel))
|
||||
alert.addAction(UIAlertAction(title: "确认核销", style: .default) { [weak self] _ in
|
||||
Task {
|
||||
guard let self else { return }
|
||||
let success = await self.viewModel.writeOff(api: self.appServices.ordersAPI, scenicId: self.appServices.accountContext.currentScenic?.id, orderNumber: item.orderNumber)
|
||||
if success { self.showToast("核销成功") }
|
||||
else if let message = self.viewModel.errorMessage { self.showToast(message) }
|
||||
}
|
||||
})
|
||||
present(alert, animated: true)
|
||||
}
|
||||
|
||||
private func refund(_ item: DepositOrderListItem) {
|
||||
let alert = UIAlertController(title: "申请退款", message: "请填写退款原因", preferredStyle: .alert)
|
||||
alert.addTextField { $0.placeholder = "退款原因" }
|
||||
alert.addAction(UIAlertAction(title: "取消", style: .cancel))
|
||||
alert.addAction(UIAlertAction(title: "提交", style: .default) { [weak self] _ in
|
||||
Task {
|
||||
guard let self else { return }
|
||||
let reason = alert.textFields?.first?.text ?? ""
|
||||
let success = await self.viewModel.refund(api: self.appServices.ordersAPI, scenicId: self.appServices.accountContext.currentScenic?.id, orderNumber: item.orderNumber, reason: reason)
|
||||
if success { self.showToast("退款申请已提交") }
|
||||
else if let message = self.viewModel.errorMessage { self.showToast(message) }
|
||||
}
|
||||
})
|
||||
present(alert, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
extension DepositOrderListViewController: UITableViewDataSource, UITableViewDelegate {
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
max(viewModel.orders.count, 1)
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
"押金订单列表 \(viewModel.orders.count)/\(viewModel.total)"
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
guard !viewModel.orders.isEmpty else {
|
||||
let cell = UITableViewCell()
|
||||
cell.textLabel?.text = viewModel.loading ? "加载中..." : "暂无押金订单"
|
||||
cell.selectionStyle = .none
|
||||
return cell
|
||||
}
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: DepositOrderCell.reuseID, for: indexPath) as! DepositOrderCell
|
||||
let item = viewModel.orders[indexPath.row]
|
||||
cell.configure(item: item, isOperating: viewModel.operatingOrderNumber == item.orderNumber)
|
||||
cell.onDetail = { [weak self] in
|
||||
self.flatMap { HomeMenuRouting.pushOrders(.depositDetail(orderNumber: item.orderNumber), from: $0) }
|
||||
}
|
||||
cell.onWriteOff = { [weak self] in self?.writeOff(item) }
|
||||
cell.onRefund = { [weak self] in self?.refund(item) }
|
||||
return cell
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||
guard indexPath.row == viewModel.orders.count - 1, viewModel.hasMore else { return }
|
||||
Task {
|
||||
await viewModel.reload(api: appServices.ordersAPI, scenicId: appServices.accountContext.currentScenic?.id, reset: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class DepositOrderCell: UITableViewCell {
|
||||
static let reuseID = "DepositOrderCell"
|
||||
var onDetail: (() -> Void)?
|
||||
var onWriteOff: (() -> Void)?
|
||||
var onRefund: (() -> Void)?
|
||||
|
||||
private let titleLabel = UILabel()
|
||||
private let statusLabel = UILabel()
|
||||
private let amountLabel = UILabel()
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
selectionStyle = .none
|
||||
titleLabel.font = .systemFont(ofSize: AppMetrics.FontSize.subheadline, weight: .semibold)
|
||||
statusLabel.font = .systemFont(ofSize: AppMetrics.FontSize.caption)
|
||||
amountLabel.font = .systemFont(ofSize: AppMetrics.FontSize.body, weight: .semibold)
|
||||
amountLabel.textColor = AppDesignUIKit.primary
|
||||
|
||||
let detailButton = UIButton(type: .system)
|
||||
detailButton.setTitle("详情", for: .normal)
|
||||
detailButton.addTarget(self, action: #selector(detailTapped), for: .touchUpInside)
|
||||
let writeOffButton = UIButton(type: .system)
|
||||
writeOffButton.setTitle("核销", for: .normal)
|
||||
writeOffButton.addTarget(self, action: #selector(writeOffTapped), for: .touchUpInside)
|
||||
let refundButton = UIButton(type: .system)
|
||||
refundButton.setTitle("退款", for: .normal)
|
||||
refundButton.addTarget(self, action: #selector(refundTapped), for: .touchUpInside)
|
||||
|
||||
let buttonRow = UIStackView(arrangedSubviews: [detailButton, writeOffButton, refundButton])
|
||||
buttonRow.axis = .horizontal
|
||||
buttonRow.distribution = .fillEqually
|
||||
|
||||
let stack = UIStackView(arrangedSubviews: [titleLabel, amountLabel, buttonRow])
|
||||
stack.axis = .vertical
|
||||
stack.spacing = 8
|
||||
contentView.addSubview(stack)
|
||||
contentView.addSubview(statusLabel)
|
||||
stack.snp.makeConstraints { make in make.edges.equalToSuperview().inset(12) }
|
||||
statusLabel.snp.makeConstraints { make in make.trailing.top.equalToSuperview().inset(12) }
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) { fatalError() }
|
||||
|
||||
func configure(item: DepositOrderListItem, isOperating: Bool) {
|
||||
titleLabel.text = item.orderNumber
|
||||
statusLabel.text = isOperating ? "处理中" : item.statusName
|
||||
amountLabel.text = "¥\(item.amount.isEmpty ? "0.00" : item.amount)"
|
||||
}
|
||||
|
||||
@objc private func detailTapped() { onDetail?() }
|
||||
@objc private func writeOffTapped() { onWriteOff?() }
|
||||
@objc private func refundTapped() { onRefund?() }
|
||||
}
|
||||
|
||||
/// 押金订单详情页。
|
||||
final class DepositOrderDetailViewController: UIViewController {
|
||||
|
||||
private let orderNumber: String
|
||||
private let viewModel = DepositOrderDetailViewModel()
|
||||
|
||||
private lazy var tableView: UITableView = {
|
||||
let table = UITableView(frame: .zero, style: .insetGrouped)
|
||||
table.dataSource = self
|
||||
table.backgroundColor = AppDesignUIKit.pageBackground
|
||||
return table
|
||||
}()
|
||||
|
||||
init(orderNumber: String) {
|
||||
self.orderNumber = orderNumber
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) { fatalError() }
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
title = "押金订单详情"
|
||||
view.addSubview(tableView)
|
||||
tableView.snp.makeConstraints { make in make.edges.equalToSuperview() }
|
||||
Task { await loadDetail() }
|
||||
}
|
||||
|
||||
private func loadDetail() async {
|
||||
await appServices.globalLoading.withLoading {
|
||||
await viewModel.load(
|
||||
api: appServices.ordersAPI,
|
||||
storeId: appServices.accountContext.currentStore?.id,
|
||||
orderNumber: orderNumber
|
||||
)
|
||||
}
|
||||
tableView.reloadData()
|
||||
if let message = viewModel.errorMessage { showToast(message) }
|
||||
}
|
||||
}
|
||||
|
||||
extension DepositOrderDetailViewController: UITableViewDataSource {
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
viewModel.detail == nil ? 1 : 8
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
|
||||
cell.selectionStyle = .none
|
||||
guard let detail = viewModel.detail else {
|
||||
cell.textLabel?.text = viewModel.errorMessage ?? (viewModel.loading ? "加载中..." : "暂无详情")
|
||||
return cell
|
||||
}
|
||||
let rows: [(String, String)] = [
|
||||
("订单号", detail.orderNumber),
|
||||
("状态", detail.orderStatusName),
|
||||
("类型", detail.orderTypeLabel),
|
||||
("付款金额", detail.actualPayAmount),
|
||||
("手机号", detail.phone),
|
||||
("项目", detail.projectName),
|
||||
("创建时间", detail.createdAt),
|
||||
("备注", detail.remark)
|
||||
]
|
||||
cell.textLabel?.text = rows[indexPath.row].0
|
||||
cell.detailTextLabel?.text = rows[indexPath.row].1
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
/// 押金拍摄信息页。
|
||||
final class DepositOrderShootingInfoViewController: UIViewController {
|
||||
|
||||
private let orderNumber: String
|
||||
private let scenicSpotId: Int
|
||||
private let photogUid: Int
|
||||
private let viewModel = DepositOrderShootingInfoViewModel()
|
||||
|
||||
private lazy var tableView: UITableView = {
|
||||
let table = UITableView(frame: .zero, style: .insetGrouped)
|
||||
table.dataSource = self
|
||||
return table
|
||||
}()
|
||||
|
||||
init(orderNumber: String, scenicSpotId: Int, photogUid: Int) {
|
||||
self.orderNumber = orderNumber
|
||||
self.scenicSpotId = scenicSpotId
|
||||
self.photogUid = photogUid
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) { fatalError() }
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
title = "押金拍摄信息"
|
||||
view.addSubview(tableView)
|
||||
tableView.snp.makeConstraints { make in make.edges.equalToSuperview() }
|
||||
Task { await loadDetail() }
|
||||
}
|
||||
|
||||
private func loadDetail() async {
|
||||
await appServices.globalLoading.withLoading {
|
||||
await viewModel.load(
|
||||
api: appServices.ordersAPI,
|
||||
storeId: appServices.accountContext.currentStore?.id,
|
||||
orderNumber: orderNumber,
|
||||
scenicSpotId: scenicSpotId,
|
||||
photogUid: photogUid
|
||||
)
|
||||
}
|
||||
tableView.reloadData()
|
||||
if let message = viewModel.errorMessage { showToast(message) }
|
||||
}
|
||||
}
|
||||
|
||||
extension DepositOrderShootingInfoViewController: UITableViewDataSource {
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
viewModel.detail == nil ? 1 : 3
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
guard let detail = viewModel.detail else { return 1 }
|
||||
switch section {
|
||||
case 0: return 2
|
||||
case 1: return detail.materialList.count
|
||||
default: return detail.completeList.count
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
guard viewModel.detail != nil else { return nil }
|
||||
switch section {
|
||||
case 1: return "素材"
|
||||
case 2: return "成片"
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
guard let detail = viewModel.detail else {
|
||||
let cell = UITableViewCell()
|
||||
cell.textLabel?.text = viewModel.errorMessage ?? "加载中..."
|
||||
return cell
|
||||
}
|
||||
if indexPath.section == 0 {
|
||||
let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
|
||||
if indexPath.row == 0 {
|
||||
cell.textLabel?.text = "打卡点"
|
||||
cell.detailTextLabel?.text = detail.scenicSpotName
|
||||
} else {
|
||||
cell.textLabel?.text = "拍摄评分"
|
||||
cell.detailTextLabel?.text = detail.orderComment.map { "\($0.starShooting) 星" } ?? "--"
|
||||
}
|
||||
return cell
|
||||
}
|
||||
let files = indexPath.section == 1 ? detail.materialList : detail.completeList
|
||||
let media = files[indexPath.row]
|
||||
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: nil)
|
||||
cell.textLabel?.text = media.fileName
|
||||
cell.detailTextLabel?.text = media.fileUrl
|
||||
return cell
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user