新增 CooperationOrder 模块(双 Tab 列表、获客员绑定、主 Tab 扫码)、订单来源/带客单绑定及配套 API 测试;同步引入 roleCode 权限匹配与相关单元测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
121 lines
3.7 KiB
Swift
121 lines
3.7 KiB
Swift
//
|
||
// HomeViewModel.swift
|
||
// suixinkan
|
||
//
|
||
// Created by Codex on 2026/6/22.
|
||
//
|
||
|
||
import Foundation
|
||
import Combine
|
||
|
||
@MainActor
|
||
/// 首页 ViewModel,根据当前角色权限构建首页菜单列表。
|
||
final class HomeViewModel: ObservableObject {
|
||
@Published private(set) var menuItems: [HomeMenuItem] = []
|
||
|
||
/// 菜单排序权重,与旧工程和 Android 端保持一致。
|
||
private let preferredOrder: [String] = [
|
||
"space_settings",
|
||
"album_list",
|
||
"album_trailer",
|
||
"wallet",
|
||
"cloud_management",
|
||
"cloud_storage_transit",
|
||
"asset_management",
|
||
"material_upload",
|
||
"task_management",
|
||
"schedule_management",
|
||
"system_settings",
|
||
"message_center",
|
||
"checkin_points",
|
||
"sample_management",
|
||
"sample_upload",
|
||
"live_stream_management",
|
||
"scenicselection",
|
||
"scenicapplication",
|
||
"permission_apply",
|
||
"permission_apply_status",
|
||
"scenic_settlement",
|
||
"scenic_settlement_review",
|
||
"verification_order",
|
||
"live_album",
|
||
"pm",
|
||
"location_report",
|
||
"registration_invitation",
|
||
"store",
|
||
"fly",
|
||
"pilot_cert",
|
||
"task_management_editor",
|
||
"deposit_order_detail",
|
||
"deposit_order",
|
||
"pm_manager",
|
||
"pilot_controller",
|
||
"/scenic-queue",
|
||
"queue_management",
|
||
"operating-area",
|
||
"/scenic-order-manage",
|
||
"photographer_orders"
|
||
]
|
||
|
||
/// 从角色权限树扁平化出菜单项,按固定顺序排序并按 URI 别名去重。
|
||
func buildMenus(from permissions: [RolePermissionResponse], currentRoleCode: String?) {
|
||
let source: RolePermissionResponse?
|
||
if let currentRoleCode, !currentRoleCode.isEmpty {
|
||
source = RolePermissionMatching.match(
|
||
in: permissions,
|
||
roleCode: currentRoleCode,
|
||
roleId: nil,
|
||
roleName: nil
|
||
)
|
||
} else {
|
||
source = permissions.first
|
||
}
|
||
|
||
guard let source else {
|
||
menuItems = []
|
||
return
|
||
}
|
||
|
||
let permissionItems = flatten(source.role.permission)
|
||
var seen = Set<String>()
|
||
var deduplicated: [PermissionItem] = []
|
||
for item in permissionItems where !item.uri.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||
if seen.insert(HomeMenuRouter.menuAliasKey(for: item.uri)).inserted {
|
||
deduplicated.append(item)
|
||
}
|
||
}
|
||
|
||
let preferredIndexMap = Dictionary(uniqueKeysWithValues: preferredOrder.enumerated().map { ($1, $0) })
|
||
let sortedItems = deduplicated.sorted { lhs, rhs in
|
||
let lhsRank = preferredIndexMap[lhs.uri] ?? Int.max
|
||
let rhsRank = preferredIndexMap[rhs.uri] ?? Int.max
|
||
if lhsRank != rhsRank {
|
||
return lhsRank < rhsRank
|
||
}
|
||
let lhsIndex = deduplicated.firstIndex(where: { $0.id == lhs.id }) ?? Int.max
|
||
let rhsIndex = deduplicated.firstIndex(where: { $0.id == rhs.id }) ?? Int.max
|
||
return lhsIndex < rhsIndex
|
||
}
|
||
|
||
menuItems = sortedItems.map { item in
|
||
HomeMenuItem(
|
||
title: item.name.isEmpty ? HomeMenuRouter.title(for: item.uri) : item.name,
|
||
uri: item.uri,
|
||
iconSrc: item.iconSrc
|
||
)
|
||
}
|
||
}
|
||
|
||
/// 返回指定 URI 的展示标题。
|
||
func title(for uri: String) -> String {
|
||
HomeMenuRouter.title(for: uri)
|
||
}
|
||
|
||
/// 递归展开权限树。
|
||
private func flatten(_ items: [PermissionItem]) -> [PermissionItem] {
|
||
items.flatMap { item in
|
||
[item] + flatten(item.children)
|
||
}
|
||
}
|
||
}
|