Initial commit: suixinkan_ios UIKit rewrite project.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -0,0 +1,285 @@
|
||||
//
|
||||
// ProjectViewControllers.swift
|
||||
// suixinkan
|
||||
//
|
||||
// Created by Codex on 2026/6/26.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
/// 摄影师项目管理页。
|
||||
final class ProjectManagementViewController: ModuleTableViewController {
|
||||
private let viewModel = ProjectManagementViewModel()
|
||||
|
||||
override func viewDidLoad() {
|
||||
title = "项目管理"
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(
|
||||
title: "新建",
|
||||
style: .plain,
|
||||
target: self,
|
||||
action: #selector(createProject)
|
||||
)
|
||||
super.viewDidLoad()
|
||||
wireViewModel(viewModel) { }
|
||||
}
|
||||
|
||||
override func tableRowCount() -> Int { viewModel.items.count }
|
||||
|
||||
override func configureCell(_ cell: TitleSubtitleTableViewCell, at indexPath: IndexPath) {
|
||||
let item = viewModel.items[indexPath.row]
|
||||
cell.configure(title: item.name, subtitle: item.statusName, detail: "¥\(item.price)")
|
||||
}
|
||||
|
||||
override func didSelectTableRow(at indexPath: IndexPath) {
|
||||
let item = viewModel.items[indexPath.row]
|
||||
navigationController?.pushViewController(
|
||||
ProjectDetailViewController(projectId: item.id, storeMode: false),
|
||||
animated: true
|
||||
)
|
||||
}
|
||||
|
||||
override func reloadContent() async {
|
||||
await viewModel.reload(api: services.projectAPI, scenicId: services.currentScenicId)
|
||||
}
|
||||
|
||||
@objc private func createProject() {
|
||||
navigationController?.pushViewController(
|
||||
ProjectEditorViewController(projectId: nil),
|
||||
animated: true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension ProjectManagementViewModel: ViewModelBindable {}
|
||||
|
||||
/// 店铺项目管理页。
|
||||
final class StoreProjectManagementViewController: ModuleTableViewController {
|
||||
private let viewModel = StoreProjectManagementViewModel()
|
||||
|
||||
override func viewDidLoad() {
|
||||
title = "店铺项目"
|
||||
super.viewDidLoad()
|
||||
wireViewModel(viewModel) { }
|
||||
}
|
||||
|
||||
override func tableRowCount() -> Int { viewModel.filteredItems.count }
|
||||
|
||||
override func configureCell(_ cell: TitleSubtitleTableViewCell, at indexPath: IndexPath) {
|
||||
let item = viewModel.filteredItems[indexPath.row]
|
||||
cell.configure(title: item.name, subtitle: item.typeName, detail: "¥\(item.price)")
|
||||
}
|
||||
|
||||
override func didSelectTableRow(at indexPath: IndexPath) {
|
||||
let item = viewModel.filteredItems[indexPath.row]
|
||||
navigationController?.pushViewController(
|
||||
ProjectDetailViewController(projectId: item.id, storeMode: true),
|
||||
animated: true
|
||||
)
|
||||
}
|
||||
|
||||
override func reloadContent() async {
|
||||
await viewModel.reload(api: services.projectAPI, userId: services.userId)
|
||||
}
|
||||
}
|
||||
|
||||
extension StoreProjectManagementViewModel: ViewModelBindable {}
|
||||
|
||||
/// 项目详情页。
|
||||
final class ProjectDetailViewController: ModuleTableViewController {
|
||||
private let projectId: Int
|
||||
private let storeMode: Bool
|
||||
private let photographerViewModel = ProjectManagementViewModel()
|
||||
private let storeViewModel = StoreProjectManagementViewModel()
|
||||
|
||||
init(projectId: Int, storeMode: Bool) {
|
||||
self.projectId = projectId
|
||||
self.storeMode = storeMode
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
title = "项目详情"
|
||||
super.viewDidLoad()
|
||||
if storeMode {
|
||||
storeViewModel.onChange = { [weak self] in self?.reloadTable() }
|
||||
} else {
|
||||
photographerViewModel.onChange = { [weak self] in self?.reloadTable() }
|
||||
}
|
||||
}
|
||||
|
||||
private var detail: PhotographerProjectDetailResponse? {
|
||||
storeMode ? storeViewModel.selectedDetail : photographerViewModel.selectedDetail
|
||||
}
|
||||
|
||||
override func tableRowCount() -> Int {
|
||||
detail == nil ? 0 : 4
|
||||
}
|
||||
|
||||
override func configureCell(_ cell: TitleSubtitleTableViewCell, at indexPath: IndexPath) {
|
||||
guard let detail else { return }
|
||||
switch indexPath.row {
|
||||
case 0: cell.configure(title: "项目名称", subtitle: detail.name)
|
||||
case 1: cell.configure(title: "简介", subtitle: detail.description)
|
||||
case 2: cell.configure(title: "价格", subtitle: detail.price)
|
||||
default: cell.configure(title: "打卡点", subtitle: "\(detail.scenicList.count) 个")
|
||||
}
|
||||
}
|
||||
|
||||
override func reloadContent() async {
|
||||
if storeMode {
|
||||
await storeViewModel.loadDetail(id: projectId, api: services.projectAPI)
|
||||
} else {
|
||||
await photographerViewModel.loadDetail(id: projectId, api: services.projectAPI)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 摄影师项目编辑页。
|
||||
final class ProjectEditorViewController: ModuleTableViewController {
|
||||
private let viewModel = ProjectEditorViewModel(mode: .create)
|
||||
private let nameField = UITextField()
|
||||
private let descriptionField = UITextField()
|
||||
private let priceField = UITextField()
|
||||
private let projectId: Int?
|
||||
|
||||
init(projectId: Int?) {
|
||||
self.projectId = projectId
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
title = projectId == nil ? "新建项目" : "编辑项目"
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(
|
||||
title: "保存",
|
||||
style: .done,
|
||||
target: self,
|
||||
action: #selector(save)
|
||||
)
|
||||
super.viewDidLoad()
|
||||
setupHeader()
|
||||
wireViewModel(viewModel) { [weak self] in self?.fillFormIfNeeded() }
|
||||
}
|
||||
|
||||
private func setupHeader() {
|
||||
nameField.placeholder = "项目名称"
|
||||
nameField.borderStyle = .roundedRect
|
||||
descriptionField.placeholder = "项目简介"
|
||||
descriptionField.borderStyle = .roundedRect
|
||||
priceField.placeholder = "价格"
|
||||
priceField.borderStyle = .roundedRect
|
||||
priceField.keyboardType = .decimalPad
|
||||
let stack = UIStackView(arrangedSubviews: [nameField, descriptionField, priceField])
|
||||
stack.axis = .vertical
|
||||
stack.spacing = 8
|
||||
stack.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: 132)
|
||||
stack.layoutMargins = UIEdgeInsets(top: 12, left: 16, bottom: 12, right: 16)
|
||||
stack.isLayoutMarginsRelativeArrangement = true
|
||||
tableView.tableHeaderView = stack
|
||||
}
|
||||
|
||||
override func tableRowCount() -> Int { 0 }
|
||||
|
||||
override func reloadContent() async {
|
||||
guard let projectId else { return }
|
||||
if let detail = try? await services.projectAPI.projectDetail(id: projectId) {
|
||||
viewModel.apply(detail)
|
||||
fillFormIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
private func fillFormIfNeeded() {
|
||||
if nameField.text?.isEmpty != false { nameField.text = viewModel.name }
|
||||
if descriptionField.text?.isEmpty != false { descriptionField.text = viewModel.descriptionText }
|
||||
if priceField.text?.isEmpty != false { priceField.text = viewModel.price }
|
||||
}
|
||||
|
||||
@objc private func save() {
|
||||
viewModel.name = nameField.text ?? ""
|
||||
viewModel.descriptionText = descriptionField.text ?? ""
|
||||
viewModel.price = priceField.text ?? ""
|
||||
Task {
|
||||
let userId = Int(services.userId ?? "")
|
||||
let success = await viewModel.submit(
|
||||
scenicId: services.currentScenicId,
|
||||
userId: userId,
|
||||
api: services.projectAPI,
|
||||
uploadService: services.ossUploadService
|
||||
)
|
||||
if success {
|
||||
navigationController?.popViewController(animated: true)
|
||||
} else if let message = viewModel.errorMessage {
|
||||
services.toastCenter.show(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ProjectEditorViewModel: ViewModelBindable {}
|
||||
|
||||
/// 店铺项目编辑页。
|
||||
final class StoreProjectEditorViewController: ModuleTableViewController {
|
||||
private let viewModel = StoreProjectEditorViewModel(mode: .create)
|
||||
private let nameField = UITextField()
|
||||
|
||||
init(projectId: Int?) {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
title = "店铺项目"
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(
|
||||
title: "保存",
|
||||
style: .done,
|
||||
target: self,
|
||||
action: #selector(save)
|
||||
)
|
||||
super.viewDidLoad()
|
||||
nameField.placeholder = "项目名称"
|
||||
nameField.borderStyle = .roundedRect
|
||||
nameField.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: 52)
|
||||
tableView.tableHeaderView = nameField
|
||||
wireViewModel(viewModel) { }
|
||||
}
|
||||
|
||||
override func tableRowCount() -> Int { viewModel.scenicList.count }
|
||||
|
||||
override func configureCell(_ cell: TitleSubtitleTableViewCell, at indexPath: IndexPath) {
|
||||
let scenic = viewModel.scenicList[indexPath.row]
|
||||
cell.configure(title: scenic.name, subtitle: viewModel.selectedScenicIds.contains(scenic.id) ? "已选择" : nil)
|
||||
}
|
||||
|
||||
override func reloadContent() async {
|
||||
guard let userId = Int(services.userId ?? "") else { return }
|
||||
await viewModel.loadScenicList(api: services.projectAPI, userId: userId)
|
||||
}
|
||||
|
||||
@objc private func save() {
|
||||
viewModel.name = nameField.text ?? ""
|
||||
Task {
|
||||
guard let userId = Int(services.userId ?? "") else { return }
|
||||
let success = await viewModel.submit(
|
||||
userId: userId,
|
||||
api: services.projectAPI,
|
||||
uploadService: services.ossUploadService
|
||||
)
|
||||
if success { navigationController?.popViewController(animated: true) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension StoreProjectEditorViewModel: ViewModelBindable {}
|
||||
Reference in New Issue
Block a user