Files
suixinkan_ios_new/suixinkan/Features/Home/Views/HomeMoreFunctionsView.swift
汉秋 5692134efc Align iOS home common apps and all-functions page with Android.
Add unified HomeCommonMenu/HomeAllFunctions diagnostics, rebuild all-functions from top-level menuList permissions, and document Home and AllFunctions module logic.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-30 09:40:02 +08:00

183 lines
7.0 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// HomeMoreFunctionsView.swift
// suixinkan
//
// Created by Codex on 2026/6/22.
//
import SwiftUI
///
struct HomeMoreFunctionsView: View {
@EnvironmentObject private var accountContext: AccountContext
@EnvironmentObject private var permissionContext: PermissionContext
@EnvironmentObject private var appRouter: AppRouter
@EnvironmentObject private var router: RouterPath
@State private var snapshot = HomeAllFunctionsSnapshot(
allFunctions: [],
commonFunctions: [],
moreFunctions: []
)
@State private var commonUris: [String] = []
private let commonMenuStore = HomeCommonMenuStore()
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 26) {
section(title: "常用应用", items: snapshot.commonFunctions, isCommon: true)
section(title: "更多功能", items: snapshot.moreFunctions, isCommon: false)
}
.padding(.horizontal, AppMetrics.Spacing.mediumLarge)
.padding(.top, AppMetrics.Spacing.medium)
.padding(.bottom, AppMetrics.Spacing.xxLarge)
}
.background(Color(hex: 0xF5F5F5))
.navigationTitle("全部功能")
.navigationBarTitleDisplayMode(.inline)
.task {
rebuildMenus()
}
.onChange(of: permissionContext.currentRole?.roleCode) { _ in
rebuildMenus()
}
.onChange(of: permissionContext.rolePermissions.count) { _ in
rebuildMenus()
}
}
private func section(title: String, items: [HomeMenuItem], isCommon: Bool) -> some View {
VStack(alignment: .leading, spacing: AppMetrics.Spacing.medium) {
Text(title)
.font(.system(size: AppMetrics.FontSize.title, weight: .bold))
.foregroundStyle(Color(hex: 0x2C2C2C))
appGrid(items: items, isCommon: isCommon)
}
}
private func appGrid(items: [HomeMenuItem], isCommon: Bool) -> some View {
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 14), count: 3), spacing: AppMetrics.Spacing.mediumLarge) {
ForEach(items, id: \.uri) { item in
ZStack(alignment: .topTrailing) {
Button {
openMenu(item)
} label: {
VStack(spacing: AppMetrics.Spacing.mediumLarge + 1) {
menuIconView(for: item)
.frame(width: 34, height: 34)
Text(item.title)
.font(.system(size: AppMetrics.FontSize.callout))
.foregroundStyle(Color(hex: 0x252525))
.lineLimit(1)
.minimumScaleFactor(0.78)
}
.frame(maxWidth: .infinity)
.frame(height: 112)
.background(.white, in: RoundedRectangle(cornerRadius: 8))
}
.buttonStyle(.plain)
Button {
updateCommonUris(isCommon: isCommon, uri: item.uri)
} label: {
Image(systemName: isCommon ? "minus.circle.fill" : "plus.circle.fill")
.font(.system(size: 24, weight: .bold))
.foregroundStyle(isCommon ? Color(hex: 0xFF1111) : AppDesign.primary)
.background(Color.white, in: Circle())
}
.buttonStyle(.plain)
.offset(x: 9, y: -9)
}
.frame(maxWidth: .infinity)
.frame(height: 112)
}
}
}
@ViewBuilder
private func menuIconView(for item: HomeMenuItem) -> some View {
RemoteImage(urlString: item.iconSrc, contentMode: .fit) {
fallbackMenuIcon(for: item.uri)
}
}
private func fallbackMenuIcon(for uri: String) -> some View {
Image(systemName: HomeIconCatalog.iconName(for: uri))
.resizable()
.scaledToFit()
.foregroundStyle(AppDesign.primary)
.frame(width: 34, height: 34)
}
private func openMenu(_ item: HomeMenuItem) {
switch HomeMenuRouter.resolve(uri: item.uri, title: item.title) {
case .tab(let tab):
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 route):
if route == .moreFunctions {
return
}
router.navigate(to: .home(route))
case .unsupported(let uri, let title, _):
router.navigate(to: .home(.modulePlaceholder(uri: uri, title: title)))
case .placeholder(let uri, let title):
HomeRouteDiagnostics.recordUnknown(uri: uri, title: title)
router.navigate(to: .home(.modulePlaceholder(uri: uri, title: title)))
}
}
///
private func updateCommonUris(isCommon: Bool, uri: String) {
let roleCode = permissionContext.currentAppRole?.rawValue
let topLevelURIs = permissionContext.topLevelPermissionURIs(for: roleCode)
if isCommon {
commonUris = commonMenuStore.remove(
uri,
current: commonUris,
roleCode: roleCode,
accountScope: accountContext.accountCachePrefix
)
} else {
commonUris = commonMenuStore.add(
uri,
current: commonUris,
topLevelPermissionURIs: topLevelURIs,
roleCode: roleCode,
accountScope: accountContext.accountCachePrefix
)
}
applySnapshot(commonURIs: commonUris)
}
private func rebuildMenus() {
let roleCode = permissionContext.currentAppRole?.rawValue
let legacyRoleId = permissionContext.currentRole?.id
let topLevelPermissions = permissionContext.topLevelPermissions(for: roleCode)
commonUris = commonMenuStore.load(
menuItems: [],
roleCode: roleCode,
accountScope: accountContext.accountCachePrefix,
legacyRoleId: legacyRoleId,
topLevelPermissionURIs: topLevelPermissions.map(\.uri)
)
applySnapshot(commonURIs: commonUris)
}
/// URI
private func applySnapshot(commonURIs: [String]) {
let roleCode = permissionContext.currentAppRole?.rawValue
snapshot = HomeAllFunctionsBuilder.build(
topLevelPermissions: permissionContext.topLevelPermissions(for: roleCode),
commonURIs: commonURIs
)
}
}