feat(ui): UI 骨架基线 — 3 Tab + RecordSheet + Quick/Archive 流程占位
替换 Xcode 默认模板: - 删除 ContentView/Item/__App - 新增 App/TijiApp(SwiftData ModelContainer)、RootView(3 Tab + RecordSheet) - DesignSystem:Tokens(色板/字体/圆角)+ Components(卡片/按钮/Chip) - Models:Indicator / Report / DiaryEntry @Model 初版 - Features:Home / Quick(A1-A3)/ Archive(B1-B5)/ Record / Trends / Me 静态 UI W2 AI 基座工作将在此基线上叠加。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
151
体己/RootView.swift
Normal file
151
体己/RootView.swift
Normal file
@@ -0,0 +1,151 @@
|
||||
import SwiftUI
|
||||
|
||||
enum TjTab: String, Hashable, CaseIterable {
|
||||
case home, trend, me
|
||||
var label: String {
|
||||
switch self {
|
||||
case .home: return "首页"
|
||||
case .trend: return "趋势"
|
||||
case .me: return "我的"
|
||||
}
|
||||
}
|
||||
var icon: String {
|
||||
switch self {
|
||||
case .home: return "house"
|
||||
case .trend: return "chart.line.uptrend.xyaxis"
|
||||
case .me: return "person.circle"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ActiveFlow: Identifiable {
|
||||
case quick, archive
|
||||
var id: String { String(describing: self) }
|
||||
}
|
||||
|
||||
struct RootView: View {
|
||||
@State private var tab: TjTab = .home
|
||||
@State private var showRecordSheet = false
|
||||
@State private var activeFlow: ActiveFlow?
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
Group {
|
||||
switch tab {
|
||||
case .home: HomeView(onTapArchive: {})
|
||||
case .trend: TrendsView()
|
||||
case .me: MeView()
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
|
||||
TabBar(active: tab,
|
||||
onTap: { tab = $0 },
|
||||
onTapRecord: { showRecordSheet = true })
|
||||
}
|
||||
.background(Tj.Palette.sand.ignoresSafeArea())
|
||||
.sheet(isPresented: $showRecordSheet) {
|
||||
RecordSheet { kind in
|
||||
showRecordSheet = false
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
||||
switch kind {
|
||||
case .quick: activeFlow = .quick
|
||||
case .archive: activeFlow = .archive
|
||||
case .diary: break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if os(iOS)
|
||||
.fullScreenCover(item: $activeFlow) { flow in
|
||||
switch flow {
|
||||
case .quick:
|
||||
QuickCaptureFlow(onClose: { activeFlow = nil })
|
||||
case .archive:
|
||||
ArchiveFlow(onClose: { activeFlow = nil })
|
||||
}
|
||||
}
|
||||
#else
|
||||
.sheet(item: $activeFlow) { flow in
|
||||
switch flow {
|
||||
case .quick:
|
||||
QuickCaptureFlow(onClose: { activeFlow = nil })
|
||||
case .archive:
|
||||
ArchiveFlow(onClose: { activeFlow = nil })
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private struct TabBar: View {
|
||||
let active: TjTab
|
||||
let onTap: (TjTab) -> Void
|
||||
let onTapRecord: () -> Void
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 0) {
|
||||
tabItem(.home)
|
||||
Color.clear.frame(width: 60, height: 1)
|
||||
tabItem(.trend)
|
||||
tabItem(.me)
|
||||
}
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.top, 10)
|
||||
.padding(.bottom, 4)
|
||||
.background(
|
||||
Tj.Palette.paper
|
||||
.overlay(alignment: .top) {
|
||||
Rectangle()
|
||||
.fill(Tj.Palette.lineSoft)
|
||||
.frame(height: 1)
|
||||
}
|
||||
)
|
||||
.overlay(alignment: .top) {
|
||||
recordButton.offset(y: -22)
|
||||
}
|
||||
}
|
||||
|
||||
private func tabItem(_ t: TjTab) -> some View {
|
||||
Button { onTap(t) } label: {
|
||||
VStack(spacing: 4) {
|
||||
Image(systemName: t.icon)
|
||||
.font(.system(size: 20, weight: .regular))
|
||||
.frame(width: 26, height: 26)
|
||||
Text(t.label)
|
||||
.font(.system(size: 11))
|
||||
}
|
||||
.foregroundStyle(active == t ? Tj.Palette.ink : Tj.Palette.text3)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 4)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
private var recordButton: some View {
|
||||
Button(action: onTapRecord) {
|
||||
VStack(spacing: 4) {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(Tj.Palette.ink)
|
||||
.shadow(color: Color(red: 0.157, green: 0.216, blue: 0.176).opacity(0.25),
|
||||
radius: 12, x: 0, y: 4)
|
||||
Image(systemName: "plus")
|
||||
.font(.system(size: 20, weight: .semibold))
|
||||
.foregroundStyle(Tj.Palette.paper)
|
||||
}
|
||||
.frame(width: 52, height: 52)
|
||||
|
||||
Text("记录")
|
||||
.font(.system(size: 11))
|
||||
.foregroundStyle(Tj.Palette.ink)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
RootView()
|
||||
}
|
||||
Reference in New Issue
Block a user