// // 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 {}