模块化 AppStore 并完善素材管理与个人空间设置。
将会话、权限、位置、收款与排队存储拆分为独立模块,同步更新各 ViewModel 与单元测试,并补充素材管理 UI 与个人空间设置交互。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -24,8 +24,8 @@ final class HomeLocationStateStore {
|
||||
|
||||
init(store: AppStore = .shared) {
|
||||
self.store = store
|
||||
reminderMinutes = store.locationReminderMinutes
|
||||
isOnline = store.onlineStatus
|
||||
reminderMinutes = store.location.reminderMinutes
|
||||
isOnline = store.location.onlineStatus
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -44,18 +44,18 @@ final class HomeLocationStateStore {
|
||||
|
||||
/// 启动时恢复在线倒计时。
|
||||
func restoreStateIfNeeded() {
|
||||
reminderMinutes = store.locationReminderMinutes
|
||||
guard store.onlineStatus else {
|
||||
reminderMinutes = store.location.reminderMinutes
|
||||
guard store.location.onlineStatus else {
|
||||
isOnline = false
|
||||
stopCountdown()
|
||||
notifyChange()
|
||||
return
|
||||
}
|
||||
let lastTime = store.lastLocationReportTime
|
||||
let lastTime = store.location.lastReportTime
|
||||
guard lastTime > 0 else {
|
||||
isOnline = false
|
||||
store.onlineStatus = false
|
||||
store.clearLastLocationReportTime()
|
||||
store.location.onlineStatus = false
|
||||
store.location.clearLastReportTime()
|
||||
stopCountdown()
|
||||
notifyChange()
|
||||
return
|
||||
@ -65,7 +65,7 @@ final class HomeLocationStateStore {
|
||||
let elapsed = now - lastTime
|
||||
if elapsed >= Self.onlineDurationMillis {
|
||||
updateOnlineStatus(false)
|
||||
store.clearLastLocationReportTime()
|
||||
store.location.clearLastReportTime()
|
||||
notifyChange()
|
||||
return
|
||||
}
|
||||
@ -81,10 +81,10 @@ final class HomeLocationStateStore {
|
||||
/// 更新在线状态并持久化。
|
||||
func updateOnlineStatus(_ online: Bool) {
|
||||
isOnline = online
|
||||
store.onlineStatus = online
|
||||
store.location.onlineStatus = online
|
||||
if !online {
|
||||
stopCountdown()
|
||||
store.clearLastLocationReportTime()
|
||||
store.location.clearLastReportTime()
|
||||
}
|
||||
notifyChange()
|
||||
}
|
||||
@ -92,16 +92,16 @@ final class HomeLocationStateStore {
|
||||
/// 更新提醒分钟数。
|
||||
func updateReminderMinutes(_ minutes: Int) {
|
||||
reminderMinutes = minutes
|
||||
store.locationReminderMinutes = minutes
|
||||
store.location.reminderMinutes = minutes
|
||||
notifyChange()
|
||||
}
|
||||
|
||||
/// 开始新的位置上报会话。
|
||||
func startLocationReport(at timestamp: Int64 = Int64(Date().timeIntervalSince1970 * 1000)) {
|
||||
anchorTimestamp = timestamp
|
||||
store.lastLocationReportTime = timestamp
|
||||
store.location.lastReportTime = timestamp
|
||||
isOnline = true
|
||||
store.onlineStatus = true
|
||||
store.location.onlineStatus = true
|
||||
elapsedSeconds = 0
|
||||
nextReportCountdownSeconds = Self.onlineDurationMillis / 1000
|
||||
startCountdown(anchorTime: timestamp)
|
||||
@ -156,7 +156,7 @@ final class HomeLocationStateStore {
|
||||
if remaining <= 0 {
|
||||
self.nextReportCountdownSeconds = 0
|
||||
self.updateOnlineStatus(false)
|
||||
self.store.clearLastLocationReportTime()
|
||||
self.store.location.clearLastReportTime()
|
||||
return
|
||||
}
|
||||
self.nextReportCountdownSeconds = max(0, remaining / 1000)
|
||||
|
||||
@ -10,10 +10,10 @@ enum HomeMenuCatalog {
|
||||
|
||||
/// 全部已知菜单项,对齐 Android `Constants.menuList`。
|
||||
static let allItems: [HomeMenuItem] = [
|
||||
HomeMenuItem(uri: "space_settings", title: "空间设置", iconName: "person.crop.square.fill"),
|
||||
HomeMenuItem(uri: "space_settings", title: "空间设置", iconName: "home_menu_space"),
|
||||
HomeMenuItem(uri: "wallet", title: "我的钱包", iconName: "creditcard.fill"),
|
||||
HomeMenuItem(uri: "cloud_management", title: "相册云盘", iconName: "icloud.fill"),
|
||||
HomeMenuItem(uri: "asset_management", title: "素材管理", iconName: "photo.on.rectangle.angled"),
|
||||
HomeMenuItem(uri: "asset_management", title: "素材管理", iconName: "home_menu_material"),
|
||||
HomeMenuItem(uri: "task_management", title: "任务管理", iconName: "checklist"),
|
||||
HomeMenuItem(uri: "task_management_editor", title: "任务管理", iconName: "checklist"),
|
||||
HomeMenuItem(uri: "schedule_management", title: "日程管理", iconName: "calendar"),
|
||||
|
||||
@ -21,7 +21,7 @@ enum HomeRouteHandler {
|
||||
return
|
||||
}
|
||||
|
||||
if uri != "pilot_controller", uri != "wallet", AppStore.shared.currentScenicId <= 0 {
|
||||
if uri != "pilot_controller", uri != "wallet", AppStore.shared.session.currentScenicId <= 0 {
|
||||
showToast("请先选择景区", from: viewController)
|
||||
return
|
||||
}
|
||||
@ -87,7 +87,7 @@ enum HomeRouteHandler {
|
||||
|
||||
@MainActor
|
||||
private static func handleOperatingArea(from viewController: UIViewController, storeItem: StoreItem?) {
|
||||
guard let role = AppStore.shared.currentAppRole else {
|
||||
guard let role = AppStore.shared.session.currentAppRole else {
|
||||
showToast("当前账号暂不支持运营区域", from: viewController)
|
||||
return
|
||||
}
|
||||
@ -99,7 +99,7 @@ enum HomeRouteHandler {
|
||||
}
|
||||
showDeveloping(from: viewController)
|
||||
case .scenicAdmin:
|
||||
guard AppStore.shared.currentScenicId > 0 else {
|
||||
guard AppStore.shared.session.currentScenicId > 0 else {
|
||||
showToast("请先选择景区", from: viewController)
|
||||
return
|
||||
}
|
||||
@ -110,7 +110,7 @@ enum HomeRouteHandler {
|
||||
}
|
||||
|
||||
private static func hasCooperationOrderPermission() -> Bool {
|
||||
let permissions = AppStore.shared.permissionItems()
|
||||
let permissions = AppStore.shared.permissions.permissionItems()
|
||||
return permissions.contains { $0.uri == "cooperation_order" }
|
||||
|| permissions.contains(where: { containsCooperationOrder(in: $0) })
|
||||
}
|
||||
|
||||
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user