
- 在ContentView中根据编译模式初始化日志级别,确保调试信息的灵活性。 - 在APILogger中使用actor封装日志级别,增强并发安全性。 - 新增通用底部Tab栏组件,优化MainPage中的底部导航逻辑,提升代码可维护性。 - 移除冗余的AppRootView和MainView,简化视图结构,提升代码整洁性。
223 lines
8.3 KiB
Swift
223 lines
8.3 KiB
Swift
//
|
||
// ContentView.swift
|
||
// yana
|
||
//
|
||
// Created by P on 2025/4/21.
|
||
//
|
||
|
||
import SwiftUI
|
||
import ComposableArchitecture
|
||
|
||
// MARK: - API Response Models
|
||
struct InitResponse: Codable, Equatable {
|
||
let status: String
|
||
let message: String?
|
||
let data: InitData?
|
||
}
|
||
|
||
struct InitData: Codable, Equatable {
|
||
let version: String?
|
||
let timestamp: Int?
|
||
let config: [String: String]?
|
||
}
|
||
|
||
// MARK: - Local UI Log Level Enum
|
||
enum UILogLevel: String, CaseIterable {
|
||
case none = "无日志"
|
||
case basic = "基础日志"
|
||
case detailed = "详细日志"
|
||
}
|
||
|
||
struct LoginTabView: View {
|
||
let store: StoreOf<LoginFeature>
|
||
let initStore: StoreOf<InitFeature>
|
||
@Binding var selectedLogLevel: APILogger.LogLevel
|
||
|
||
var body: some View {
|
||
VStack {
|
||
// 日志级别选择器
|
||
VStack(alignment: .leading, spacing: 8) {
|
||
Text("日志级别:")
|
||
.font(.headline)
|
||
.foregroundColor(.primary)
|
||
Picker("日志级别", selection: $selectedLogLevel) {
|
||
Text("无日志").tag(APILogger.LogLevel.none)
|
||
Text("基础日志").tag(APILogger.LogLevel.basic)
|
||
Text("详细日志").tag(APILogger.LogLevel.detailed)
|
||
}
|
||
.pickerStyle(SegmentedPickerStyle())
|
||
}
|
||
.padding()
|
||
.background(Color.gray.opacity(0.1))
|
||
.cornerRadius(10)
|
||
Spacer()
|
||
VStack(spacing: 20) {
|
||
Text("eparty")
|
||
.font(.largeTitle)
|
||
.fontWeight(.bold)
|
||
VStack(spacing: 15) {
|
||
TextField("账号", text: Binding(
|
||
get: { store.account },
|
||
set: { store.send(.updateAccount($0)) }
|
||
))
|
||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||
.autocorrectionDisabled(true)
|
||
SecureField("密码", text: Binding(
|
||
get: { store.password },
|
||
set: { store.send(.updatePassword($0)) }
|
||
))
|
||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||
}
|
||
.padding(.horizontal)
|
||
if let error = store.error {
|
||
Text(error)
|
||
.foregroundColor(.red)
|
||
.font(.caption)
|
||
.multilineTextAlignment(.center)
|
||
.padding(.horizontal)
|
||
}
|
||
VStack(spacing: 10) {
|
||
Button(action: {
|
||
store.send(.login)
|
||
}) {
|
||
HStack {
|
||
if store.isLoading {
|
||
ProgressView()
|
||
.scaleEffect(0.8)
|
||
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
||
}
|
||
Text(store.isLoading ? "登录中..." : "登录")
|
||
}
|
||
.frame(maxWidth: .infinity)
|
||
.padding()
|
||
.background(store.isLoading ? Color.gray : Color.blue)
|
||
.foregroundColor(.white)
|
||
.cornerRadius(10)
|
||
}
|
||
.disabled(store.isLoading || store.account.isEmpty || store.password.isEmpty)
|
||
Button(action: {
|
||
initStore.send(.initialize)
|
||
}) {
|
||
HStack {
|
||
if initStore.isLoading {
|
||
ProgressView()
|
||
.scaleEffect(0.8)
|
||
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
||
}
|
||
Text(initStore.isLoading ? "测试中..." : "测试初始化")
|
||
}
|
||
.frame(maxWidth: .infinity)
|
||
.padding()
|
||
.background(initStore.isLoading ? Color.gray : Color.green)
|
||
.foregroundColor(.white)
|
||
.cornerRadius(10)
|
||
}
|
||
.disabled(initStore.isLoading)
|
||
if let response = initStore.response {
|
||
VStack(alignment: .leading, spacing: 8) {
|
||
HStack {
|
||
Text("API 测试结果:")
|
||
.font(.headline)
|
||
.foregroundColor(.primary)
|
||
}
|
||
ScrollView {
|
||
VStack(alignment: .leading, spacing: 4) {
|
||
Text("状态: \(response.status)")
|
||
if let message = response.message {
|
||
Text("消息: \(message)")
|
||
}
|
||
if let data = response.data {
|
||
Text("版本: \(data.version ?? "未知")")
|
||
Text("时间戳: \(data.timestamp ?? 0)")
|
||
if let config = data.config {
|
||
Text("配置:")
|
||
ForEach(Array(config.keys), id: \.self) { key in
|
||
Text(" \(key): \(config[key] ?? "")")
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.font(.system(.caption, design: .monospaced))
|
||
.foregroundColor(.secondary)
|
||
.frame(maxWidth: .infinity, alignment: .leading)
|
||
.padding(8)
|
||
.background(Color.gray.opacity(0.1))
|
||
.cornerRadius(8)
|
||
}
|
||
.frame(maxHeight: 200)
|
||
}
|
||
.padding()
|
||
.background(Color.gray.opacity(0.05))
|
||
.cornerRadius(10)
|
||
}
|
||
if let error = initStore.error {
|
||
Text(error)
|
||
.foregroundColor(.red)
|
||
.font(.caption)
|
||
.multilineTextAlignment(.center)
|
||
.padding()
|
||
}
|
||
}
|
||
.padding(.horizontal)
|
||
}
|
||
Spacer()
|
||
}
|
||
.padding()
|
||
}
|
||
}
|
||
|
||
struct ContentView: View {
|
||
let store: StoreOf<LoginFeature>
|
||
let initStore: StoreOf<InitFeature>
|
||
let configStore: StoreOf<ConfigFeature>
|
||
@State private var selectedLogLevel: APILogger.LogLevel = {
|
||
// 以编译期默认值初始化(与 APILogger.Config 一致)
|
||
#if DEBUG
|
||
return .detailed
|
||
#else
|
||
return .none
|
||
#endif
|
||
}()
|
||
@State private var selectedTab = 0
|
||
|
||
var body: some View {
|
||
WithPerceptionTracking {
|
||
TabView(selection: $selectedTab) {
|
||
LoginTabView(store: store, initStore: initStore, selectedLogLevel: $selectedLogLevel)
|
||
.tabItem {
|
||
Label("登录", systemImage: "person.circle")
|
||
}
|
||
.tag(0)
|
||
ConfigView(store: configStore)
|
||
.tabItem {
|
||
Label("API 测试", systemImage: "network")
|
||
}
|
||
.tag(1)
|
||
}
|
||
.onChange(of: selectedLogLevel) { _, selectedLogLevel in
|
||
Task { await APILogger.Config.shared.set(selectedLogLevel) }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#Preview {
|
||
ContentView(
|
||
store: Store(
|
||
initialState: LoginFeature.State()
|
||
) {
|
||
LoginFeature()
|
||
},
|
||
initStore: Store(
|
||
initialState: InitFeature.State()
|
||
) {
|
||
InitFeature()
|
||
},
|
||
configStore: Store(
|
||
initialState: ConfigFeature.State()
|
||
) {
|
||
ConfigFeature()
|
||
}
|
||
)
|
||
}
|