新增门店订单与核销管理列表,对齐 Android 订单能力并完善账号级缓存。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -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 自己的导航路径。
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
/// 读取并清空待处理的全局扫码结果,确保同一次扫码只被处理一次。
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
Reference in New Issue
Block a user