Initial commit: suixinkan_ios UIKit rewrite project.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-26 14:33:31 +08:00
commit 9edf993432
297 changed files with 47151 additions and 0 deletions

View File

@ -0,0 +1,181 @@
//
// AppRouteViewControllerFactory.swift
// suixinkan
//
import UIKit
@MainActor
/// ViewController `AppRoute` UIKit
enum AppRouteViewControllerFactory {
/// ViewController使
static func makeViewController(for route: AppRoute, services: AppServices) -> UIViewController {
switch route {
case .placeholder(let title):
return PlaceholderViewController(title: title)
case .home(let homeRoute):
return makeViewController(for: homeRoute, services: services)
case .profile(let profileRoute):
return makeViewController(for: profileRoute, services: services)
case .orders(let ordersRoute):
return makeViewController(for: ordersRoute, services: services)
}
}
/// ViewController
static func makeViewController(for route: HomeRoute, services: AppServices) -> UIViewController {
switch route {
case .profileSpace:
return ProfileViewController()
case .scenicSelection:
return ScenicSelectionViewController()
case .permissionApply:
return PermissionApplyViewController()
case .permissionApplyStatus:
return PermissionApplyStatusViewController()
case .scenicApplication:
return ScenicApplicationViewController()
case .moreFunctions:
return HomeMoreFunctionsViewController()
case .settings:
return SettingsViewController()
case .paymentCollection:
return PaymentCollectionViewController()
case .wallet:
return WalletViewController()
case .taskManagement:
return TaskManagementViewController()
case .taskCreate:
return TaskCreateViewController()
case .taskDetail(let id, let summary):
return TaskDetailViewController(taskId: id, summary: summary)
case .projectManagement:
return ProjectManagementViewController()
case .pmProjectManagement:
return StoreProjectManagementViewController()
case .projectDetail(let id, let storeMode):
return ProjectDetailViewController(projectId: id, storeMode: storeMode)
case .projectEditor(let id, let storeMode):
if storeMode {
return StoreProjectEditorViewController(projectId: id)
}
return ProjectEditorViewController(projectId: id)
case .scheduleManagement:
return ScheduleManagementViewController()
case .scheduleAdd:
return ScheduleAddViewController()
case .photographerInvite:
return PhotographerInviteViewController()
case .inviteRecord:
return InviteRecordViewController()
case .cloudStorage:
return CloudStorageViewController()
case .cloudStorageTransit:
return CloudStorageTransitViewController()
case .materialLibrary:
return MediaLibraryViewController(kind: .material)
case .materialUpload:
return MediaLibraryUploadViewController(kind: .material)
case .sampleLibrary:
return MediaLibraryViewController(kind: .sample)
case .sampleUpload:
return MediaLibraryUploadViewController(kind: .sample)
case .albumList:
return AlbumListViewController()
case .albumTrailer:
return AlbumTrailerViewController()
case .punchPointList:
return PunchPointListViewController()
case .punchPointDetail(let id, let summary):
return PunchPointDetailViewController(punchPointId: id, summary: summary)
case .punchPointEditor(let id):
return PunchPointEditorViewController(punchPointId: id)
case .punchPointQR(_, let title, let qrURL):
return PunchPointQRViewController(title: title, qrURL: qrURL)
case .locationReport:
return LocationReportViewController()
case .locationReportHistory:
return LocationReportHistoryViewController()
case .depositOrders:
return DepositOrderListViewController()
case .withdrawalAudit:
return WithdrawalAuditViewController()
case .scenicSettlement:
return ScenicSettlementViewController()
case .scenicSettlementReview:
return ScenicSettlementReviewViewController()
case .messageCenter:
return MessageCenterViewController()
case .queueManagement:
return QueueManagementViewController()
case .liveManagement:
return LiveManagementViewController()
case .liveAlbum:
return LiveAlbumViewController()
case .operatingArea:
return OperatingAreaViewController()
case .pilotCertification:
return PilotCertificationViewController()
case .modulePlaceholder(_, let title):
return FeaturePlaceholderViewController(title: title, uri: homeRouteURI(route))
}
}
/// ViewController
static func makeViewController(for route: ProfileRoute, services: AppServices) -> UIViewController {
switch route {
case .accountSwitch:
return AccountSwitchViewController()
case .realNameAuth:
return RealNameAuthViewController()
case .settings:
return SettingsViewController()
case .agreement(let page):
return AgreementViewController(page: page)
#if DEBUG
case .debugHomeMenus:
return HomeMoreFunctionsViewController()
#endif
}
}
/// ViewController
static func makeViewController(for route: OrdersRoute, services: AppServices) -> UIViewController {
switch route {
case .storeDetail(let item):
return StoreOrderDetailViewController(item: item)
case .writeOffDetail(let item):
return WriteOffOrderDetailViewController(item: item)
case .depositDetail(let orderNumber):
return DepositOrderDetailViewController(orderNumber: orderNumber)
case .depositShootingInfo(let orderNumber, let scenicSpotId, let photogUid):
return DepositOrderShootingInfoViewController(
orderNumber: orderNumber,
scenicSpotId: scenicSpotId,
photogUid: photogUid
)
case .historicalShooting(let orderNumber):
return HistoricalShootingInfoViewController(orderNumber: orderNumber)
case .multiTravelTaskUpload(let orderNumber):
return MultiTravelTaskUploadViewController(initialOrderNumber: orderNumber)
case .orderTrailer(let orderNumber, let title):
return FeaturePlaceholderViewController(title: title, uri: "order_trailer:\(orderNumber)")
}
}
private static func homeRouteURI(_ route: HomeRoute) -> String {
if case let .modulePlaceholder(uri, _) = route {
return uri
}
return String(describing: route)
}
}
@MainActor
/// ViewController `UIKitAppNavigation`
enum HomeRouteViewControllerFactory {
static func make(for route: HomeRoute) -> UIViewController {
AppRouteViewControllerFactory.makeViewController(for: route, services: AppServices.shared)
}
}

View File

@ -0,0 +1,58 @@
//
// AppTab.swift
// suixinkan
//
// Created by Codex on 2026/6/18.
//
import UIKit
/// Tab
enum AppTab: String, CaseIterable, Identifiable, Hashable {
case home
case orders
case statistics
case profile
var id: Self { self }
var title: String {
switch self {
case .home:
"首页"
case .orders:
"订单"
case .statistics:
"数据"
case .profile:
"我的"
}
}
var systemImage: String {
switch self {
case .home:
"house"
case .orders:
"doc.text"
case .statistics:
"chart.bar"
case .profile:
"person"
}
}
/// Tab ViewController
func makeRootViewController(services: AppServices) -> UIViewController {
switch self {
case .home:
return HomeViewController()
case .orders:
return OrdersViewController()
case .statistics:
return StatisticsViewController()
case .profile:
return ProfileViewController()
}
}
}

View File

@ -0,0 +1,109 @@
//
// NavigationRouter.swift
// suixinkan
//
// Created by Codex on 2026/6/18.
//
import Foundation
/// Tab
enum AppRoute: Hashable {
case placeholder(title: String)
case home(HomeRoute)
case profile(ProfileRoute)
case orders(OrdersRoute)
/// push TabBar
var hidesTabBarWhenPushed: Bool {
true
}
}
@MainActor
/// Tab
final class RouterPath {
var onChange: (() -> Void)?
var path: [AppRoute] = [] { didSet { onChange?() } }
/// Tab
func navigate(to route: AppRoute) {
path.append(route)
}
/// Tab
func navigateBack() {
guard !path.isEmpty else { return }
path.removeLast()
}
/// Tab
func reset() {
path = []
}
}
@MainActor
/// Tab Tab
final class AppRouter {
var onChange: (() -> Void)?
var selectedTab: AppTab = .home { didSet { onChange?() } }
var selectedOrdersEntry: OrdersEntry = .storeOrders { didSet { onChange?() } }
private(set) var pendingOrderScanCode: String? { didSet { onChange?() } }
private let routers: [AppTab: RouterPath]
init() {
var builtRouters: [AppTab: RouterPath] = [:]
for tab in AppTab.allCases {
builtRouters[tab] = RouterPath()
}
routers = builtRouters
routers.values.forEach { router in
router.onChange = { [weak self] in
self?.onChange?()
}
}
}
/// Tab
func router(for tab: AppTab) -> RouterPath {
guard let router = routers[tab] else {
assertionFailure("Missing router path for tab: \(tab)")
return RouterPath()
}
return router
}
/// Tab
func select(_ tab: AppTab) {
selectedTab = tab
}
/// Tab
func selectOrders(entry: OrdersEntry) {
selectedOrdersEntry = entry
selectedTab = .orders
}
///
func routeToOrderVerification(scannedCode: String) {
pendingOrderScanCode = scannedCode
selectOrders(entry: .verificationOrders)
}
///
func consumePendingOrderScanCode() -> String? {
let code = pendingOrderScanCode
pendingOrderScanCode = nil
return code
}
/// Tab Tab
func reset() {
selectedTab = .home
selectedOrdersEntry = .storeOrders
pendingOrderScanCode = nil
routers.values.forEach { $0.reset() }
}
}

View File

@ -0,0 +1,111 @@
//
// TabNavigationController.swift
// suixinkan
//
import UIKit
@MainActor
/// Tab `AppRouter` push TabBar
final class TabNavigationController: UINavigationController, UINavigationControllerDelegate {
let appTab: AppTab
private let services: AppServices
private var isSyncingStack = false
weak var tabBarHost: MainTabBarController?
init(tab: AppTab, services: AppServices) {
self.appTab = tab
self.services = services
super.init(nibName: nil, bundle: nil)
delegate = self
navigationBar.prefersLargeTitles = false
setViewControllers([tab.makeRootViewController(services: services)], animated: false)
registerNavigationBridge()
services.appRouter.router(for: appTab).onChange = { [weak self] in
self?.syncFromRouterPath()
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
/// Push RouterPath
func pushRoute(_ route: AppRoute, animated: Bool = true) {
let viewController = AppRouteViewControllerFactory.makeViewController(for: route, services: services)
viewController.hidesBottomBarWhenPushed = route.hidesTabBarWhenPushed
isSyncingStack = true
services.appRouter.router(for: appTab).navigate(to: route)
isSyncingStack = false
pushViewController(viewController, animated: animated)
}
func navigationController(
_ navigationController: UINavigationController,
willShow viewController: UIViewController,
animated: Bool
) {
let isRoot = viewController === viewControllers.first
tabBarHost?.setCustomTabBarHidden(!isRoot, animated: animated)
}
func navigationController(
_ navigationController: UINavigationController,
didShow viewController: UIViewController,
animated: Bool
) {
syncRouterPathFromStack()
}
private func registerNavigationBridge() {
switch appTab {
case .home:
UIKitAppNavigation.homeNavigationController = self
case .profile:
UIKitAppNavigation.profileNavigationController = self
default:
break
}
}
private func syncFromRouterPath() {
guard !isSyncingStack else { return }
let routerPath = services.appRouter.router(for: appTab)
let targetDepth = routerPath.path.count
let currentDepth = max(viewControllers.count - 1, 0)
if targetDepth > currentDepth {
for index in currentDepth..<targetDepth {
let route = routerPath.path[index]
let viewController = AppRouteViewControllerFactory.makeViewController(for: route, services: services)
viewController.hidesBottomBarWhenPushed = route.hidesTabBarWhenPushed
pushViewController(viewController, animated: true)
}
return
}
if targetDepth < currentDepth {
if targetDepth == 0 {
popToRootViewController(animated: true)
} else if targetDepth < viewControllers.count {
popToViewController(viewControllers[targetDepth], animated: true)
}
}
}
private func syncRouterPathFromStack() {
guard !isSyncingStack else { return }
let routerPath = services.appRouter.router(for: appTab)
let depth = max(viewControllers.count - 1, 0)
if depth < routerPath.path.count {
isSyncingStack = true
routerPath.path = Array(routerPath.path.prefix(depth))
isSyncingStack = false
}
}
}

View File

@ -0,0 +1,46 @@
//
// UIKitAppNavigation.swift
// suixinkan
//
// Created by Codex on 2026/6/26.
//
import UIKit
@MainActor
/// UIKit AppRouter UI Test
enum UIKitAppNavigation {
weak static var homeNavigationController: UINavigationController?
weak static var profileNavigationController: UINavigationController?
/// Push
static func pushHomeRoute(_ route: HomeRoute, animated: Bool = true) {
let viewController = AppRouteViewControllerFactory.makeViewController(for: route, services: AppServices.shared)
homeNavigationController?.pushViewController(viewController, animated: animated)
}
/// Push
static func pushProfileRoute(_ route: ProfileRoute, animated: Bool = true) {
let viewController = AppRouteViewControllerFactory.makeViewController(
for: .profile(route),
services: AppServices.shared
)
profileNavigationController?.pushViewController(viewController, animated: animated)
}
}
extension AppRouter {
/// UIKit push
func navigateHome(_ route: HomeRoute, animated: Bool = true) {
select(.home)
router(for: .home).navigate(to: .home(route))
UIKitAppNavigation.pushHomeRoute(route, animated: animated)
}
/// UIKit push
func navigateProfile(_ route: ProfileRoute, animated: Bool = true) {
select(.profile)
router(for: .profile).navigate(to: .profile(route))
UIKitAppNavigation.pushProfileRoute(route, animated: animated)
}
}