Unify bottom actions, selection controls, navigation structure, spacing, and theme colors across iOS pages. Replace the app icon with a higher-contrast native-style asset and include its source SVG.
104 lines
3.7 KiB
Swift
104 lines
3.7 KiB
Swift
import SwiftUI
|
|
|
|
struct SettingsView: View {
|
|
@StateObject private var model: SettingsViewModel
|
|
@State private var showsClearDialog = false
|
|
|
|
init(model: SettingsViewModel? = nil) {
|
|
_model = StateObject(wrappedValue: model ?? DefaultContainer.makeSettingsModel())
|
|
}
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
LazyVStack(spacing: 12) {
|
|
if let errorMessage = model.errorMessage {
|
|
AIStatusBanner(errorMessage, tint: AITheme.danger, systemImage: "exclamationmark.triangle")
|
|
}
|
|
if let notice = model.notice {
|
|
AIStatusBanner(notice, tint: AITheme.success, systemImage: "checkmark.circle")
|
|
}
|
|
|
|
runtimeCard
|
|
storageCard
|
|
securityCard
|
|
}
|
|
.padding(16)
|
|
.padding(.bottom, 8)
|
|
}
|
|
.background(AITheme.canvas.ignoresSafeArea())
|
|
.navigationTitle("设置")
|
|
.toolbar {
|
|
ToolbarItemGroup(placement: .primaryAction) {
|
|
AIToolbarButton("刷新", systemImage: "arrow.clockwise") {
|
|
Task { await model.refresh() }
|
|
}
|
|
.disabled(model.isLoading)
|
|
}
|
|
}
|
|
.refreshable {
|
|
await model.refresh()
|
|
}
|
|
.confirmationDialog(
|
|
"确认清除运行日志?此操作不可恢复。",
|
|
isPresented: $showsClearDialog,
|
|
titleVisibility: .visible
|
|
) {
|
|
Button("清除运行日志", role: .destructive) {
|
|
Task { await model.clearRuntimeLogs() }
|
|
}
|
|
Button("取消", role: .cancel) {}
|
|
}
|
|
.task {
|
|
await model.refresh()
|
|
}
|
|
}
|
|
|
|
private var runtimeCard: some View {
|
|
AICard {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
AISectionTitle("应用信息", systemImage: "info.circle")
|
|
if let runtime = model.runtime {
|
|
AIKeyValueRow("版本", value: "\(runtime.marketingVersion) (\(runtime.buildVersion))")
|
|
AIKeyValueRow("设备", value: runtime.deviceModel)
|
|
} else {
|
|
Text("正在读取应用信息…")
|
|
.font(.subheadline)
|
|
.foregroundStyle(AITheme.muted)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private var storageCard: some View {
|
|
AICard {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
AISectionTitle("本地数据", systemImage: "externaldrive")
|
|
AIKeyValueRow("存储位置", value: "仅保存在本机应用沙盒")
|
|
AIKeyValueRow("当前占用", value: model.storageLabel)
|
|
Button {
|
|
showsClearDialog = true
|
|
} label: {
|
|
Label("清除运行日志", systemImage: "trash")
|
|
}
|
|
.buttonStyle(AIButtonStyle(kind: .danger, size: .regular, fillsWidth: true))
|
|
}
|
|
}
|
|
}
|
|
|
|
private var securityCard: some View {
|
|
AICard {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
AISectionTitle("安全与登录", systemImage: "lock.shield")
|
|
AIKeyValueRow("实例密码", value: "保存在本机应用沙盒")
|
|
AIKeyValueRow("登录会话", value: "仅保存在内存")
|
|
AIStatusBanner(
|
|
"实例凭据只保存在本机应用沙盒,登录会话只保留在内存中,应用关闭后自动失效。",
|
|
tint: AITheme.primary,
|
|
systemImage: "shield.checkered"
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|