新增门店订单与核销管理列表,对齐 Android 订单能力并完善账号级缓存。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-29 14:46:10 +08:00
parent c4de3d17d0
commit 08492df6ce
51 changed files with 3668 additions and 102 deletions

View File

@ -15,7 +15,7 @@ App 模块负责应用入口、根视图切换、全局状态注入、主导航
- `suixinkanApp`SwiftUI 应用入口,挂载 `RootView`
- `RootView`:创建 `AppSession``AccountContext``PermissionContext``ScenicSpotContext``AppRouter``ToastCenter``APIClient` 和业务 API并注入 SwiftUI Environment。
- `AppSession`:保存认证阶段和正式 token只负责登录态不承载业务资料。
- `AccountContext`:保存当前账号资料、景区作用域和门店作用域。
- `AccountContext`:保存当前账号资料、账号类型、景区作用域和门店作用域,并提供账号级缓存前缀
- `PermissionContext`:保存角色权限、当前角色和扁平化权限 URI。
- `ScenicSpotContext`:保存当前景区下的景点/打卡点列表和加载状态。
- `AppRouter`:保存当前 Tab 和每个 Tab 自己的导航路径。

View File

@ -46,6 +46,9 @@ enum AppUITestRouteDriver {
appRouter.select(tab)
case .orders(let entry):
appRouter.selectOrders(entry: entry)
case .orderPush(let route):
appRouter.select(.orders)
appRouter.router(for: .orders).navigate(to: .orders(route))
case .destination(let homeRoute):
appRouter.select(.home)
appRouter.router(for: .home).navigate(to: .home(homeRoute))

View File

@ -51,6 +51,8 @@ extension OrdersRoute {
MultiTravelTaskUploadView(initialOrderNumber: orderNumber)
case .orderTrailer(let orderNumber, let title):
OrderTrailerView(orderNumber: orderNumber, title: title)
case .writeOffList:
WriteOffListView()
}
}
}
@ -107,10 +109,17 @@ final class AppRouter: ObservableObject {
selectedTab = .orders
}
///
///
func routeToOrderVerification(scannedCode: String) {
pendingOrderScanCode = scannedCode
selectOrders(entry: .verificationOrders)
selectedTab = .orders
router(for: .orders).navigate(to: .orders(.writeOffList))
}
///
func openWriteOffList() {
selectedTab = .orders
router(for: .orders).navigate(to: .orders(.writeOffList))
}
///

View File

@ -6,6 +6,7 @@
//
import Combine
import Foundation
///
struct AccountProfile: Codable, Equatable {
@ -61,14 +62,24 @@ struct BusinessScope: Codable, Equatable, Identifiable {
/// /
final class AccountContext: ObservableObject {
@Published private(set) var profile: AccountProfile?
@Published private(set) var accountType: String?
@Published private(set) var scenicScopes: [BusinessScope] = []
@Published private(set) var storeScopes: [BusinessScope] = []
@Published var currentScenic: BusinessScope?
@Published var currentStore: BusinessScope?
/// Android
var accountCachePrefix: String? {
let userId = profile?.userId.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
guard !userId.isEmpty else { return nil }
let type = accountType?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
return type.isEmpty ? userId : "\(type)_\(userId)"
}
///
func applyLogin(profile: AccountProfile? = nil) {
func applyLogin(profile: AccountProfile? = nil, accountType: String? = nil) {
self.profile = profile
self.accountType = accountType?.trimmingCharacters(in: .whitespacesAndNewlines)
}
///
@ -122,6 +133,7 @@ final class AccountContext: ObservableObject {
/// 退
func reset() {
profile = nil
accountType = nil
scenicScopes = []
storeScopes = []
currentScenic = nil

View File

@ -63,7 +63,7 @@ final class AuthSessionCoordinator {
let scenicScopes = response.scenicScopes
let storeScopes = response.storeScopes
let identity = response.currentAccountIdentity
accountContext.applyLogin(profile: profile)
accountContext.applyLogin(profile: profile, accountType: identity?.accountType)
accountContext.replaceScopes(scenic: scenicScopes, stores: storeScopes)
appSession.beginRestoring(token: token)
@ -89,7 +89,7 @@ final class AuthSessionCoordinator {
appSession.markLoggedIn(token: token)
saveSnapshot(
profile: accountContext.profile ?? profile,
accountType: identity?.accountType,
accountType: accountContext.accountType ?? identity?.accountType,
businessUserId: identity?.businessUserId,
permissionContext: permissionContext,
accountContext: accountContext
@ -99,7 +99,7 @@ final class AuthSessionCoordinator {
saveSnapshot(
profile: accountContext.profile ?? profile,
accountType: identity?.accountType,
accountType: accountContext.accountType ?? identity?.accountType,
businessUserId: identity?.businessUserId,
permissionContext: permissionContext,
accountContext: accountContext
@ -113,7 +113,7 @@ final class AuthSessionCoordinator {
accountContext.replaceProfile(profile)
saveSnapshot(
profile: profile,
accountType: snapshotStore.load()?.accountType,
accountType: accountContext.accountType ?? snapshotStore.load()?.accountType,
businessUserId: snapshotStore.load()?.businessUserId,
currentRoleCode: snapshotStore.load()?.currentRoleCode,
accountContext: accountContext

View File

@ -83,7 +83,7 @@ final class SessionBootstrapper {
///
private func restoreCachedSnapshot(to accountContext: AccountContext) -> AccountSnapshot? {
guard let snapshot = snapshotStore.load() else { return nil }
accountContext.applyLogin(profile: snapshot.profile)
accountContext.applyLogin(profile: snapshot.profile, accountType: snapshot.accountType)
accountContext.replaceScopes(
scenic: snapshot.scenicScopes,
stores: snapshot.storeScopes,
@ -99,7 +99,7 @@ final class SessionBootstrapper {
snapshotStore.save(
AccountSnapshot(
profile: accountContext.profile,
accountType: existing?.accountType,
accountType: accountContext.accountType ?? existing?.accountType,
businessUserId: existing?.businessUserId,
currentRoleCode: permissionContext.currentAppRole?.rawValue ?? existing?.currentRoleCode,
scenicScopes: accountContext.scenicScopes,