模块化 AppStore 并完善素材管理与个人空间设置。

将会话、权限、位置、收款与排队存储拆分为独立模块,同步更新各 ViewModel 与单元测试,并补充素材管理 UI 与个人空间设置交互。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-13 11:01:08 +08:00
parent ceca780ab3
commit 005349f8e6
128 changed files with 2953 additions and 1123 deletions

View File

@ -27,10 +27,10 @@ final class AllFunctionsViewModel {
///
func loadFunctions() {
let permissions = appStore.permissionItems()
let permissions = appStore.permissions.permissionItems()
let allMenus = HomeMenuCatalog.visibleMenus(from: permissions)
let accountScope = appStore.accountCachePrefix
let roleCode = appStore.roleCode
let accountScope = appStore.session.accountCachePrefix
let roleCode = appStore.session.roleCode
let savedURIs = commonMenuStore.savedCommonURIs(accountScope: accountScope, roleCode: roleCode)
let defaultURIs = HomeCommonMenuStore.defaultCommonURIs(from: permissions)
let commonURIs = savedURIs.isEmpty ? defaultURIs : savedURIs
@ -71,8 +71,8 @@ final class AllFunctionsViewModel {
private func persistCommonMenus() {
commonMenuStore.saveCommonURIs(
commonMenus.map(\.uri),
accountScope: appStore.accountCachePrefix,
roleCode: appStore.roleCode
accountScope: appStore.session.accountCachePrefix,
roleCode: appStore.session.roleCode
)
}

View File

@ -94,7 +94,7 @@ final class HomeViewModel {
/// role-permission
func loadPermissions(api: HomeAPI, force: Bool = false) async {
if !force, !appStore.permissionItems().isEmpty {
if !force, !appStore.permissions.permissionItems().isEmpty {
rebuildCommonMenus()
await loadStoreListIfNeeded(api: api)
notifyStateChange()
@ -104,21 +104,21 @@ final class HomeViewModel {
do {
let rolePermissionList = try await api.rolePermissions()
if rolePermissionList.isEmpty {
appStore.savePermissionItems([])
appStore.permissions.savePermissionItems([])
showPermissionDialog = true
rebuildCommonMenus()
notifyStateChange()
return
}
appStore.saveRolePermissionList(rolePermissionList)
let matched = appStore.matchRolePermissionItem(in: rolePermissionList) ?? rolePermissionList[0]
appStore.permissions.saveRolePermissionList(rolePermissionList)
let matched = appStore.permissions.matchRolePermissionItem(in: rolePermissionList) ?? rolePermissionList[0]
if !matched.role.name.isEmpty {
appStore.roleName = matched.role.name
appStore.session.roleName = matched.role.name
}
appStore.saveRoleScenicList(matched.scenic)
appStore.permissions.saveRoleScenicList(matched.scenic)
let permissions = matched.role.permission.map(HomePermissionItem.init)
appStore.savePermissionItems(permissions)
appStore.permissions.savePermissionItems(permissions)
showPermissionDialog = false
refreshLocalDisplayState()
rebuildCommonMenus()
@ -127,7 +127,7 @@ final class HomeViewModel {
} catch is CancellationError {
return
} catch {
appStore.savePermissionItems([])
appStore.permissions.savePermissionItems([])
showPermissionDialog = true
rebuildCommonMenus()
onShowMessage?(error.localizedDescription)
@ -142,7 +142,7 @@ final class HomeViewModel {
notifyStateChange()
return
}
guard appStore.currentScenicId > 0 else {
guard appStore.session.currentScenicId > 0 else {
storeItem = nil
notifyStateChange()
return
@ -150,13 +150,13 @@ final class HomeViewModel {
do {
let response = try await api.storeList()
let scenicId = appStore.currentScenicId
let savedStoreId = appStore.currentStoreId
let scenicId = appStore.session.currentScenicId
let savedStoreId = appStore.session.currentStoreId
let savedMatch = savedStoreId > 0 ? response.list.first { $0.id == savedStoreId } : nil
let matched = savedMatch ?? response.list.first { $0.scenicId == scenicId }
storeItem = matched
if let matched {
appStore.currentStoreId = matched.id
appStore.session.currentStoreId = matched.id
}
notifyStateChange()
} catch is CancellationError {
@ -173,7 +173,7 @@ final class HomeViewModel {
showScenicDialog = false
return
}
let hasScenic = appStore.currentScenicId > 0
let hasScenic = appStore.session.currentScenicId > 0
showScenicDialog = !hasScenic
if showScenicDialog || dismissedLocationTimeoutDialog {
showLocationTimeoutDialog = false
@ -196,7 +196,7 @@ final class HomeViewModel {
notifyStateChange()
return
}
let staffId = appStore.userId.trimmingCharacters(in: .whitespacesAndNewlines)
let staffId = appStore.session.userId.trimmingCharacters(in: .whitespacesAndNewlines)
guard !staffId.isEmpty else {
showLocationTimeoutDialog = true
notifyStateChange()
@ -225,25 +225,25 @@ final class HomeViewModel {
/// Android `initCurrentScenicName`
func refreshLocalDisplayState() {
let scenicId = appStore.currentScenicId
let scenicName = appStore.currentScenicName.trimmingCharacters(in: .whitespacesAndNewlines)
let scenicId = appStore.session.currentScenicId
let scenicName = appStore.session.currentScenicName.trimmingCharacters(in: .whitespacesAndNewlines)
if scenicId > 0, !scenicName.isEmpty {
currentScenicName = scenicName
} else {
currentScenicName = ""
appStore.currentScenicId = 0
appStore.currentScenicName = ""
appStore.session.currentScenicId = 0
appStore.session.currentScenicName = ""
}
currentAppRole = appStore.currentAppRole
currentAppRole = appStore.session.currentAppRole
isMinimalTopRole = currentAppRole?.usesHomeMinimalTopLayout ?? false
}
///
func rebuildCommonMenus() {
let permissions = appStore.permissionItems()
let permissions = appStore.permissions.permissionItems()
let allMenus = HomeMenuCatalog.visibleMenus(from: permissions)
let accountScope = appStore.accountCachePrefix
let roleCode = appStore.roleCode
let accountScope = appStore.session.accountCachePrefix
let roleCode = appStore.session.roleCode
let savedURIs = commonMenuStore.savedCommonURIs(accountScope: accountScope, roleCode: roleCode)
if savedURIs.isEmpty, !permissions.isEmpty {

View File

@ -21,7 +21,7 @@ final class PermissionApplyStatusViewModel {
init(applyCode: String, appStore: AppStore = .shared) {
self.applyCode = applyCode
self.appStore = appStore
rolePermissionList = appStore.rolePermissionList()
rolePermissionList = appStore.permissions.rolePermissionList()
}
func loadPendingData(api: HomeAPI) async {

View File

@ -45,7 +45,7 @@ final class PermissionApplyViewModel {
}
func loadRolesFromLocal() {
let rolePermissionList = appStore.rolePermissionList()
let rolePermissionList = appStore.permissions.rolePermissionList()
var seen = Set<Int>()
availableRoles = rolePermissionList.compactMap { item in
guard !seen.contains(item.role.id) else { return nil }
@ -158,7 +158,7 @@ final class PermissionApplyViewModel {
///
func existingScenics(for roleId: Int) -> [ScenicInfo] {
appStore.rolePermissionList()
appStore.permissions.rolePermissionList()
.filter { $0.role.id == roleId }
.flatMap(\.scenic)
}

View File

@ -43,7 +43,7 @@ final class ScenicSelectionViewModel {
searchQuery: searchQuery,
currentLat: currentLat,
currentLng: currentLng,
selectedScenicId: appStore.currentScenicId
selectedScenicId: appStore.session.currentScenicId
)
notifyStateChange()
}
@ -73,8 +73,8 @@ final class ScenicSelectionViewModel {
}
func selectSpot(_ spot: ScenicSpotDisplayItem) {
appStore.currentScenicId = spot.id
appStore.currentScenicName = spot.name
appStore.session.currentScenicId = spot.id
appStore.session.currentScenicName = spot.name
NotificationCenter.default.post(
name: NotificationName.scenicDidChange,
object: nil,
@ -109,11 +109,11 @@ final class ScenicSelectionViewModel {
}
private func resolvedScenicList() -> [ScenicInfo] {
let cached = appStore.roleScenicList()
let cached = appStore.permissions.roleScenicList()
if !cached.isEmpty { return cached }
let rolePermissionList = appStore.rolePermissionList()
let rolePermissionList = appStore.permissions.rolePermissionList()
guard !rolePermissionList.isEmpty else { return [] }
return appStore.matchRolePermissionItem(in: rolePermissionList)?.scenic ?? []
return appStore.permissions.matchRolePermissionItem(in: rolePermissionList)?.scenic ?? []
}
private func notifyStateChange() {