
- 新增MainView Tab切换问题分析文档,详细描述问题原因及解决方案。 - 优化BottomTabView的绑定逻辑,简化状态管理,确保Tab切换时状态正确更新。 - 在MeView中实现用户信息加载逻辑调整,确保动态列表仅在首次进入时加载,并添加错误处理视图。 - 创建EmptyStateView组件,提供统一的空状态展示和重试功能。 - 增强调试信息输出,便于后续问题排查和用户体验提升。
239 lines
7.1 KiB
Swift
239 lines
7.1 KiB
Swift
import SwiftUI
|
|
import ComposableArchitecture
|
|
|
|
struct ConfigView: View {
|
|
let store: StoreOf<ConfigFeature>
|
|
|
|
var body: some View {
|
|
NavigationView {
|
|
VStack(spacing: 20) {
|
|
Text(LocalizedString("config.api_test", comment: ""))
|
|
.font(.largeTitle)
|
|
.fontWeight(.bold)
|
|
.padding(.top)
|
|
|
|
// 状态显示
|
|
Group {
|
|
if store.isLoading {
|
|
LoadingView()
|
|
} else if store.errorMessage != nil {
|
|
ConfigErrorView(store: store)
|
|
} else if let configData = store.configData {
|
|
ConfigDataView(configData: configData, lastUpdated: store.lastUpdated)
|
|
} else {
|
|
// EmptyStateView()
|
|
}
|
|
}
|
|
|
|
Spacer()
|
|
|
|
// 操作按钮
|
|
ActionButtonsView(store: store)
|
|
|
|
}
|
|
}
|
|
.navigationBarHidden(true)
|
|
}
|
|
}
|
|
|
|
// MARK: - Loading View
|
|
struct LoadingView: View {
|
|
var body: some View {
|
|
VStack {
|
|
ProgressView()
|
|
.scaleEffect(1.2)
|
|
Text(LocalizedString("config.loading", comment: ""))
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
.padding(.top, 8)
|
|
}
|
|
.frame(height: 100)
|
|
}
|
|
}
|
|
|
|
// MARK: - Error View
|
|
struct ConfigErrorView: View {
|
|
let store: StoreOf<ConfigFeature>
|
|
|
|
var body: some View {
|
|
VStack(spacing: 16) {
|
|
Image(systemName: "exclamationmark.triangle.fill")
|
|
.font(.system(size: 40))
|
|
.foregroundColor(.yellow)
|
|
Text(LocalizedString("config.error", comment: ""))
|
|
.foregroundColor(.red)
|
|
Button(LocalizedString("config.clear_error", comment: "")) {
|
|
store.send(.clearError)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Config Data View
|
|
struct ConfigDataView: View {
|
|
let configData: ConfigData
|
|
let lastUpdated: Date?
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
VStack(alignment: .leading, spacing: 16) {
|
|
if let version = configData.version {
|
|
InfoRow(title: LocalizedString("config.version", comment: ""), value: version)
|
|
}
|
|
|
|
if let features = configData.features, !features.isEmpty {
|
|
FeaturesSection(features: features)
|
|
}
|
|
|
|
if let settings = configData.settings {
|
|
SettingsSection(settings: settings)
|
|
}
|
|
|
|
if let lastUpdated = lastUpdated {
|
|
Text(String(format: LocalizedString("config.last_updated", comment: ""), {
|
|
let formatter = DateFormatter()
|
|
formatter.dateStyle = .medium
|
|
formatter.timeStyle = .short
|
|
return formatter.string(from: lastUpdated)
|
|
}()))
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
.frame(maxWidth: .infinity, alignment: .center)
|
|
}
|
|
}
|
|
.padding()
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Features Section
|
|
struct FeaturesSection: View {
|
|
let features: [String]
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text(LocalizedString("config.feature_list", comment: ""))
|
|
.font(.headline)
|
|
.fontWeight(.semibold)
|
|
|
|
ForEach(features, id: \.self) { feature in
|
|
HStack {
|
|
Circle()
|
|
.fill(Color.green)
|
|
.frame(width: 6, height: 6)
|
|
Text(feature)
|
|
.font(.body)
|
|
}
|
|
}
|
|
}
|
|
.padding()
|
|
.background(Color(.systemGray6))
|
|
.cornerRadius(12)
|
|
}
|
|
}
|
|
|
|
// MARK: - Settings Section
|
|
struct SettingsSection: View {
|
|
let settings: ConfigSettings
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text(LocalizedString("config.settings", comment: ""))
|
|
.font(.headline)
|
|
.fontWeight(.semibold)
|
|
|
|
if let enableDebug = settings.enableDebug {
|
|
InfoRow(title: LocalizedString("config.debug_mode", comment: ""), value: enableDebug ? "启用" : "禁用")
|
|
}
|
|
|
|
if let apiTimeout = settings.apiTimeout {
|
|
InfoRow(title: LocalizedString("config.api_timeout", comment: ""), value: "\(apiTimeout)秒")
|
|
}
|
|
|
|
if let maxRetries = settings.maxRetries {
|
|
InfoRow(title: LocalizedString("config.max_retries", comment: ""), value: "\(maxRetries)")
|
|
}
|
|
}
|
|
.padding()
|
|
.background(Color(.systemGray6))
|
|
.cornerRadius(12)
|
|
}
|
|
}
|
|
|
|
// MARK: - Empty State View
|
|
//struct EmptyStateView: View {
|
|
// var body: some View {
|
|
// VStack(spacing: 16) {
|
|
// Image(systemName: "arrow.down.circle")
|
|
// .font(.system(size: 40))
|
|
// .foregroundColor(.blue)
|
|
// Text(LocalizedString("config.click_to_load", comment: ""))
|
|
// .font(.body)
|
|
// .multilineTextAlignment(.center)
|
|
// .foregroundColor(.secondary)
|
|
// }
|
|
// .frame(maxHeight: .infinity)
|
|
// }
|
|
//}
|
|
|
|
// MARK: - Action Buttons View
|
|
struct ActionButtonsView: View {
|
|
let store: StoreOf<ConfigFeature>
|
|
|
|
var body: some View {
|
|
VStack(spacing: 12) {
|
|
Button(action: {
|
|
store.send(.loadConfig)
|
|
}) {
|
|
HStack {
|
|
if store.isLoading {
|
|
ProgressView()
|
|
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
|
.scaleEffect(0.8)
|
|
} else {
|
|
Image(systemName: "arrow.clockwise")
|
|
}
|
|
Text(store.isLoading ? "加载中..." : "加载配置")
|
|
}
|
|
}
|
|
.buttonStyle(.borderedProminent)
|
|
.disabled(store.isLoading)
|
|
.frame(maxWidth: .infinity)
|
|
.frame(height: 50)
|
|
|
|
Text(LocalizedString("config.use_new_tca", comment: ""))
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Helper Views
|
|
struct InfoRow: View {
|
|
let title: String
|
|
let value: String
|
|
|
|
var body: some View {
|
|
HStack {
|
|
Text(title)
|
|
.font(.body)
|
|
.fontWeight(.medium)
|
|
|
|
Spacer()
|
|
|
|
Text(value)
|
|
.font(.body)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Preview
|
|
//#Preview {
|
|
// ConfigView(
|
|
// store: Store(initialState: ConfigFeature.State()) {
|
|
// ConfigFeature()
|
|
// }
|
|
// )
|
|
//}
|