从 iOS 17 Observation 迁移至 iOS 16 兼容的 Combine 架构
将最低部署版本降至 iOS 16,以 ObservableObject 替换 @Observable,新增导航与 UI 兼容层,并补充登录冒烟 UI 测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -11,17 +11,17 @@ import UniformTypeIdentifiers
|
||||
|
||||
/// 摄影师项目管理页面,展示项目列表并提供新建、详情和删除入口。
|
||||
struct ProjectManagementView: View {
|
||||
@Environment(AccountContext.self) private var accountContext
|
||||
@Environment(AccountSnapshotStore.self) private var snapshotStore
|
||||
@Environment(ProjectAPI.self) private var projectAPI
|
||||
@EnvironmentObject private var accountContext: AccountContext
|
||||
@Environment(\.accountSnapshotStore) private var snapshotStore
|
||||
@Environment(\.projectAPI) private var projectAPI
|
||||
@Environment(\.globalLoading) private var globalLoading
|
||||
@State private var viewModel = ProjectManagementViewModel()
|
||||
@StateObject private var viewModel = ProjectManagementViewModel()
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
filterBar
|
||||
if accountContext.currentScenic?.id == nil {
|
||||
ContentUnavailableView("缺少景区", systemImage: "map", description: Text("请选择景区后再管理项目。"))
|
||||
AppContentUnavailableView("缺少景区", systemImage: "map", description: Text("请选择景区后再管理项目。"))
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
} else {
|
||||
projectList
|
||||
@ -66,7 +66,7 @@ struct ProjectManagementView: View {
|
||||
ScrollView {
|
||||
LazyVStack(spacing: AppMetrics.Spacing.medium) {
|
||||
if viewModel.items.isEmpty && !viewModel.loading {
|
||||
ContentUnavailableView("暂无项目", systemImage: "folder", description: Text("可点击右上角创建项目。"))
|
||||
AppContentUnavailableView("暂无项目", systemImage: "folder", description: Text("可点击右上角创建项目。"))
|
||||
.frame(maxWidth: .infinity, minHeight: 260)
|
||||
}
|
||||
ForEach(viewModel.items) { item in
|
||||
@ -110,10 +110,10 @@ struct ProjectManagementView: View {
|
||||
|
||||
/// 店铺项目管理页面,展示店铺项目列表、筛选和新建入口。
|
||||
struct StoreProjectManagementView: View {
|
||||
@Environment(AccountSnapshotStore.self) private var snapshotStore
|
||||
@Environment(ProjectAPI.self) private var projectAPI
|
||||
@Environment(\.accountSnapshotStore) private var snapshotStore
|
||||
@Environment(\.projectAPI) private var projectAPI
|
||||
@Environment(\.globalLoading) private var globalLoading
|
||||
@State private var viewModel = StoreProjectManagementViewModel()
|
||||
@StateObject private var viewModel = StoreProjectManagementViewModel()
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
@ -127,7 +127,7 @@ struct StoreProjectManagementView: View {
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
if viewModel.filteredItems.isEmpty && !viewModel.loading {
|
||||
ContentUnavailableView("暂无店铺项目", systemImage: "folder", description: Text("可点击右上角新建店铺项目。"))
|
||||
AppContentUnavailableView("暂无店铺项目", systemImage: "folder", description: Text("可点击右上角新建店铺项目。"))
|
||||
.frame(minHeight: 260)
|
||||
}
|
||||
}
|
||||
@ -196,7 +196,7 @@ struct StoreProjectManagementView: View {
|
||||
|
||||
/// 项目详情页面,展示项目基础信息、媒体、打卡点和摄影师摘要。
|
||||
struct ProjectDetailView: View {
|
||||
@Environment(ProjectAPI.self) private var projectAPI
|
||||
@Environment(\.projectAPI) private var projectAPI
|
||||
@Environment(\.globalLoading) private var globalLoading
|
||||
let projectId: Int
|
||||
let storeMode: Bool
|
||||
@ -209,7 +209,7 @@ struct ProjectDetailView: View {
|
||||
if let detail {
|
||||
ProjectDetailContentView(detail: detail)
|
||||
} else if let errorMessage {
|
||||
ContentUnavailableView("加载失败", systemImage: "exclamationmark.triangle", description: Text(errorMessage))
|
||||
AppContentUnavailableView("加载失败", systemImage: "exclamationmark.triangle", description: Text(errorMessage))
|
||||
.frame(minHeight: 360)
|
||||
} else {
|
||||
ProgressView()
|
||||
@ -247,14 +247,14 @@ struct ProjectDetailView: View {
|
||||
/// 摄影师项目编辑页面,支持新建和编辑项目。
|
||||
struct ProjectEditorView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@Environment(AccountContext.self) private var accountContext
|
||||
@Environment(AccountSnapshotStore.self) private var snapshotStore
|
||||
@Environment(ScenicSpotContext.self) private var scenicSpotContext
|
||||
@Environment(ProjectAPI.self) private var projectAPI
|
||||
@Environment(OSSUploadService.self) private var uploadService
|
||||
@EnvironmentObject private var accountContext: AccountContext
|
||||
@Environment(\.accountSnapshotStore) private var snapshotStore
|
||||
@EnvironmentObject private var scenicSpotContext: ScenicSpotContext
|
||||
@Environment(\.projectAPI) private var projectAPI
|
||||
@Environment(\.ossUploadService) private var uploadService
|
||||
@Environment(\.globalLoading) private var globalLoading
|
||||
let projectId: Int?
|
||||
@State private var viewModel = ProjectEditorViewModel(mode: .create)
|
||||
@StateObject private var viewModel = ProjectEditorViewModel(mode: .create)
|
||||
@State private var coverItem: PhotosPickerItem?
|
||||
@State private var carouselItems: [PhotosPickerItem] = []
|
||||
@State private var loadingDetail = false
|
||||
@ -291,10 +291,10 @@ struct ProjectEditorView: View {
|
||||
.task {
|
||||
await prepare()
|
||||
}
|
||||
.onChange(of: coverItem) { _, newValue in
|
||||
.onChange(of: coverItem) { newValue in
|
||||
Task { await loadCover(newValue) }
|
||||
}
|
||||
.onChange(of: carouselItems) { _, newValue in
|
||||
.onChange(of: carouselItems) { newValue in
|
||||
Task { await loadCarousel(newValue) }
|
||||
}
|
||||
}
|
||||
@ -387,7 +387,7 @@ struct ProjectEditorView: View {
|
||||
defer { loadingDetail = false }
|
||||
do {
|
||||
let detail = try await projectAPI.projectDetail(id: projectId)
|
||||
viewModel = ProjectEditorViewModel(mode: .edit(detail))
|
||||
viewModel.apply(detail)
|
||||
} catch {
|
||||
viewModel.errorMessage = error.localizedDescription
|
||||
}
|
||||
@ -418,12 +418,12 @@ struct ProjectEditorView: View {
|
||||
/// 店铺项目编辑页面,支持新建和编辑店铺项目。
|
||||
struct StoreProjectEditorView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@Environment(AccountSnapshotStore.self) private var snapshotStore
|
||||
@Environment(ProjectAPI.self) private var projectAPI
|
||||
@Environment(OSSUploadService.self) private var uploadService
|
||||
@Environment(\.accountSnapshotStore) private var snapshotStore
|
||||
@Environment(\.projectAPI) private var projectAPI
|
||||
@Environment(\.ossUploadService) private var uploadService
|
||||
@Environment(\.globalLoading) private var globalLoading
|
||||
let projectId: Int?
|
||||
@State private var viewModel = StoreProjectEditorViewModel(mode: .create)
|
||||
@StateObject private var viewModel = StoreProjectEditorViewModel(mode: .create)
|
||||
@State private var coverItem: PhotosPickerItem?
|
||||
@State private var carouselItems: [PhotosPickerItem] = []
|
||||
|
||||
@ -454,10 +454,10 @@ struct StoreProjectEditorView: View {
|
||||
}
|
||||
}
|
||||
.task { await prepare() }
|
||||
.onChange(of: coverItem) { _, newValue in
|
||||
.onChange(of: coverItem) { newValue in
|
||||
Task { viewModel.coverImage = await ProjectPickerLoader.loadImage(from: newValue) }
|
||||
}
|
||||
.onChange(of: carouselItems) { _, newValue in
|
||||
.onChange(of: carouselItems) { newValue in
|
||||
Task { viewModel.carouselImages = await ProjectPickerLoader.loadImages(from: newValue) }
|
||||
}
|
||||
}
|
||||
@ -545,7 +545,7 @@ struct StoreProjectEditorView: View {
|
||||
if let projectId {
|
||||
do {
|
||||
let detail = try await projectAPI.storeManagerProjectDetail(id: projectId)
|
||||
viewModel = StoreProjectEditorViewModel(mode: .edit(detail))
|
||||
viewModel.apply(detail)
|
||||
} catch {
|
||||
viewModel.errorMessage = error.localizedDescription
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user