feat(ios): unify action hierarchy and surfaces
Refine primary and toolbar actions, align row buttons, normalize status surfaces, and split storage from destructive cleanup. Verified with Swift tests and the simulator build.
This commit is contained in:
@@ -137,7 +137,8 @@ struct FleetView: View {
|
||||
.buttonStyle(
|
||||
AIButtonStyle(
|
||||
kind: .secondary,
|
||||
size: .compact
|
||||
size: .regular,
|
||||
fixedWidth: 96
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -55,7 +55,11 @@ struct LogsView: View {
|
||||
.navigationTitle("日志中心")
|
||||
.toolbar {
|
||||
ToolbarItemGroup(placement: .primaryAction) {
|
||||
AIToolbarButton("刷新", systemImage: "arrow.clockwise") {
|
||||
AIToolbarButton(
|
||||
"刷新",
|
||||
systemImage: "arrow.clockwise",
|
||||
kind: .primary
|
||||
) {
|
||||
Task { await model.refresh() }
|
||||
}
|
||||
.disabled(model.isLoading)
|
||||
|
||||
@@ -56,7 +56,7 @@ struct NotificationsView: View {
|
||||
Text(
|
||||
model.isRetryingAll
|
||||
? "投递中"
|
||||
: (model.overview?.queue.total == 0 ? "暂无待投递" : "立即投递")
|
||||
: (model.overview?.queue.total == 0 ? "没有待投递" : "立即投递")
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -198,7 +198,11 @@ struct NotificationsView: View {
|
||||
Label("重试", systemImage: "arrow.clockwise")
|
||||
}
|
||||
.buttonStyle(
|
||||
AIButtonStyle(kind: .secondary, size: .compact, fillsWidth: true)
|
||||
AIButtonStyle(
|
||||
kind: .secondary,
|
||||
size: .compact,
|
||||
fixedWidth: AITheme.listActionButtonWidth
|
||||
)
|
||||
)
|
||||
.disabled(model.busyQueueID != nil)
|
||||
|
||||
@@ -207,7 +211,13 @@ struct NotificationsView: View {
|
||||
} label: {
|
||||
Label("删除", systemImage: "trash")
|
||||
}
|
||||
.buttonStyle(AIButtonStyle(kind: .danger, size: .compact, fillsWidth: true))
|
||||
.buttonStyle(
|
||||
AIButtonStyle(
|
||||
kind: .danger,
|
||||
size: .compact,
|
||||
fixedWidth: AITheme.listActionButtonWidth
|
||||
)
|
||||
)
|
||||
.disabled(model.busyQueueID != nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,103 +1,116 @@
|
||||
import SwiftUI
|
||||
|
||||
struct SettingsView: View {
|
||||
@StateObject private var model: SettingsViewModel
|
||||
@State private var showsClearDialog = false
|
||||
@StateObject private var model: SettingsViewModel
|
||||
@State private var showsClearDialog = false
|
||||
|
||||
init(model: SettingsViewModel? = nil) {
|
||||
_model = StateObject(wrappedValue: model ?? DefaultContainer.makeSettingsModel())
|
||||
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
|
||||
dangerZoneCard
|
||||
}
|
||||
.padding(16)
|
||||
.padding(.bottom, 8)
|
||||
}
|
||||
|
||||
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
|
||||
.background(AITheme.canvas.ignoresSafeArea())
|
||||
.navigationTitle("设置")
|
||||
.toolbar {
|
||||
ToolbarItemGroup(placement: .primaryAction) {
|
||||
AIToolbarButton(
|
||||
"刷新",
|
||||
systemImage: "arrow.clockwise",
|
||||
kind: .primary
|
||||
) {
|
||||
Button("清除运行日志", role: .destructive) {
|
||||
Task { await model.clearRuntimeLogs() }
|
||||
}
|
||||
Button("取消", role: .cancel) {}
|
||||
}
|
||||
.task {
|
||||
await model.refresh()
|
||||
Task { await model.refresh() }
|
||||
}
|
||||
.disabled(model.isLoading)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
.refreshable {
|
||||
await model.refresh()
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
.confirmationDialog(
|
||||
"确认清除运行日志?此操作不可恢复。",
|
||||
isPresented: $showsClearDialog,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("清除运行日志", role: .destructive) {
|
||||
Task { await model.clearRuntimeLogs() }
|
||||
}
|
||||
Button("取消", role: .cancel) {}
|
||||
}
|
||||
|
||||
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"
|
||||
)
|
||||
}
|
||||
}
|
||||
.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var dangerZoneCard: some View {
|
||||
AICard {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
AISectionTitle("数据清理", systemImage: "trash.square", detail: "不可恢复")
|
||||
Button {
|
||||
showsClearDialog = true
|
||||
} label: {
|
||||
Label("清除运行日志", systemImage: "trash")
|
||||
}
|
||||
.buttonStyle(AIButtonStyle(kind: .danger, size: .regular, fillsWidth: true))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user