Files
suixinkan_ios_new/suixinkan/Features/TravelAlbum/Views/WiredTransfer/WiredTransferControlCard.swift
汉秋 ceac8ba67e 优化有线传图页设备展示,并缓存传输模式选项。
未连接状态改为「未连接相机」;设备型号映射为可读名称而非 iPhone12,1 等内部代号;边拍边传/拍后传输按账号持久化并在下次进入时恢复。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-30 14:07:34 +08:00

239 lines
8.4 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// WiredTransferControlCard.swift
// suixinkan
//
import SwiftUI
/// 线 + + Tab
struct WiredTransferControlCard: View {
let scenicSpotLabel: String?
let deviceStorageInfo: DeviceStorageSnapshot
let isCameraConnected: Bool
let isRefreshingCameraFiles: Bool
let cameraDeviceName: String
let transferModeOption: String
let tabTitles: [String]
let tabCounts: [Int]
let selectedTabIndex: Int
let onDeviceUsageClick: () -> Void
let onRefreshCameraFiles: () -> Void
let onHelpClick: () -> Void
let onTransferModeSelected: (String) -> Void
let onTabSelected: (Int) -> Void
private let modeOptions = [
WiredCameraTransferViewModel.modeLiveCapture,
WiredCameraTransferViewModel.modePostShootTransfer
]
var body: some View {
VStack(spacing: 0) {
VStack(spacing: 12) {
connectionStatusRow
filterChipRow
}
.padding(.horizontal, 14)
.padding(.vertical, 14)
Divider().background(WiredTransferDesign.borderLight)
WiredTransferStatsTabRow(
tabTitles: tabTitles,
counts: tabCounts,
selectedTabIndex: selectedTabIndex,
onTabSelected: onTabSelected
)
}
.background(Color.white)
.clipShape(RoundedRectangle(cornerRadius: WiredTransferDesign.cardCornerRadius))
.shadow(color: WiredTransferDesign.cardShadow, radius: 6, x: 0, y: 2)
.padding(.horizontal, WiredTransferDesign.cardHorizontalPadding)
.offset(y: -WiredTransferDesign.cardOverlap)
}
private var connectionStatusRow: some View {
HStack(alignment: .top, spacing: 10) {
deviceUsagePanel
VStack(alignment: .leading, spacing: 4) {
HStack(alignment: .center, spacing: 8) {
connectionChip
Spacer(minLength: 4)
refreshButton
}
if !isCameraConnected {
helpDocLine
}
if let scenicSpotLabel, !scenicSpotLabel.isEmpty {
Text(scenicSpotLabel)
.font(.system(size: 10))
.foregroundStyle(WiredTransferDesign.text999)
.lineLimit(1)
}
}
}
}
private var deviceUsagePanel: some View {
Button(action: onDeviceUsageClick) {
VStack(spacing: 6) {
Text(deviceStorageInfo.modelName)
.font(.system(size: 11, weight: .medium))
.foregroundStyle(WiredTransferDesign.text333)
.multilineTextAlignment(.center)
.lineLimit(2)
.frame(maxWidth: 88)
Text(deviceStorageInfo.availableGbText)
.font(.system(size: 10))
.foregroundStyle(AppDesign.primary)
.lineLimit(1)
}
.frame(minWidth: WiredTransferDesign.statusRingMinWidth)
.padding(.horizontal, 8)
.padding(.vertical, 10)
.overlay {
RoundedRectangle(cornerRadius: 12)
.stroke(WiredTransferDesign.borderLight, lineWidth: 3)
}
}
.buttonStyle(.plain)
}
private var connectionChip: some View {
Text(connectionChipText)
.font(.system(size: 11, weight: .medium))
.foregroundStyle(isCameraConnected ? AppDesign.primary : WiredTransferDesign.danger)
.lineLimit(1)
.padding(.horizontal, 8)
.padding(.vertical, 2)
.background((isCameraConnected ? AppDesign.primary : WiredTransferDesign.danger).opacity(0.1))
.clipShape(RoundedRectangle(cornerRadius: 4))
}
private var connectionChipText: String {
if isCameraConnected {
let name = cameraDeviceName.isEmpty ? "已连接相机" : cameraDeviceName
return name
}
return "未连接相机"
}
private var refreshButton: some View {
Button(action: onRefreshCameraFiles) {
Text(refreshButtonTitle)
.font(.system(size: 12))
.foregroundStyle(.white)
.padding(.horizontal, 10)
.frame(minWidth: 64)
.frame(height: 34)
.background(refreshButtonColor)
.clipShape(RoundedRectangle(cornerRadius: 8))
}
.buttonStyle(.plain)
.disabled(!isCameraConnected || isRefreshingCameraFiles)
.opacity(isCameraConnected && !isRefreshingCameraFiles ? 1 : 0.85)
}
private var refreshButtonTitle: String {
if isRefreshingCameraFiles { return "读取中" }
if isCameraConnected { return "刷新" }
return "等待连接"
}
private var refreshButtonColor: Color {
if !isCameraConnected { return WiredTransferDesign.danger }
if isRefreshingCameraFiles { return WiredTransferDesign.text999 }
return AppDesign.primary
}
private var helpDocLine: some View {
Button(action: onHelpClick) {
HStack(spacing: 0) {
Text("未连接,查看")
.foregroundStyle(WiredTransferDesign.text666)
Text("《帮助文档》")
.foregroundStyle(AppDesign.primary)
}
.font(.system(size: 10))
.frame(maxWidth: .infinity, alignment: .leading)
}
.buttonStyle(.plain)
}
private var filterChipRow: some View {
GeometryReader { geometry in
let totalWeight: CGFloat = 1 + 0.72 + 1.15
let spacing: CGFloat = 6
let available = geometry.size.width - spacing * 2
HStack(spacing: spacing) {
WiredTransferFilterChip(label: "不修图", options: [], enabled: false, onSelected: { _ in })
.frame(width: available * 1 / totalWeight)
WiredTransferFilterChip(
label: WiredCameraTransferViewModel.formatJPG,
options: [],
enabled: false,
onSelected: { _ in }
)
.frame(width: available * 0.72 / totalWeight)
WiredTransferFilterChip(
label: transferModeOption,
options: modeOptions,
onSelected: onTransferModeSelected
)
.frame(width: available * 1.15 / totalWeight)
}
}
.frame(height: WiredTransferDesign.filterChipHeight)
}
}
/// Tab Android TransferStatsTabRow
struct WiredTransferStatsTabRow: View {
let tabTitles: [String]
let counts: [Int]
let selectedTabIndex: Int
let onTabSelected: (Int) -> Void
var body: some View {
HStack(spacing: 0) {
ForEach(Array(tabTitles.enumerated()), id: \.offset) { index, title in
if index > 0 {
Divider()
.frame(height: 32)
.background(WiredTransferDesign.borderLight)
}
tabItem(index: index, title: title)
}
}
.frame(height: WiredTransferDesign.tabRowHeight)
}
private func tabItem(index: Int, title: String) -> some View {
Button {
onTabSelected(index)
} label: {
VStack(spacing: 2) {
Text("\(counts.indices.contains(index) ? counts[index] : 0)")
.font(.system(size: 18, weight: .semibold))
.foregroundStyle(countColor(for: index))
Text(title)
.font(.system(size: 13))
.foregroundStyle(selectedTabIndex == index ? AppDesign.primary : WiredTransferDesign.text666)
RoundedRectangle(cornerRadius: 1)
.fill(selectedTabIndex == index ? AppDesign.primary : Color.clear)
.frame(width: 24, height: 2)
.padding(.top, 2)
}
.frame(maxWidth: .infinity)
.padding(.vertical, 8)
}
.buttonStyle(.plain)
}
private func countColor(for index: Int) -> Color {
if selectedTabIndex == index { return AppDesign.primary }
if index == 2 { return WiredTransferDesign.danger }
return WiredTransferDesign.text333
}
}