import SwiftUI import SwiftData /// 「我的 · 自定义指标」管理页。 /// 从 MeView 进入;集中查看 / 新建 / 编辑 / 删除自定义长期监测指标。 struct CustomMetricsListView: View { @Query(sort: \CustomMonitorMetric.createdAt, order: .reverse) private var metrics: [CustomMonitorMetric] @Query private var indicators: [Indicator] @State private var editingTarget: CustomMetricEditTarget? var body: some View { ScrollView { VStack(alignment: .leading, spacing: 12) { hintBanner if metrics.isEmpty { emptyState } else { ForEach(metrics) { m in Button { editingTarget = CustomMetricEditTarget(metric: m) } label: { row(m) } .buttonStyle(.plain) } } } .padding(.horizontal, 16) .padding(.top, 8) .padding(.bottom, 32) } .background(Tj.Palette.sand.ignoresSafeArea()) .navigationTitle("自定义指标") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button { editingTarget = CustomMetricEditTarget(metric: nil) } label: { Image(systemName: "plus") .font(.system(size: 16, weight: .semibold)) } } } .sheet(item: $editingTarget) { target in CustomMetricEditor(existing: target.metric) { _ in } } } // MARK: - subviews private var hintBanner: some View { HStack(spacing: 10) { Image(systemName: "info.circle.fill") .foregroundStyle(Tj.Palette.text3) Text("自定义指标会出现在「+ 指标记录 → 长期监测」的 grid 里,可设提醒、进趋势") .font(.system(size: 12)) .foregroundStyle(Tj.Palette.text2) .fixedSize(horizontal: false, vertical: true) Spacer(minLength: 0) } .padding(12) .background( RoundedRectangle(cornerRadius: Tj.Radius.sm, style: .continuous) .fill(Tj.Palette.sand2.opacity(0.5)) ) } private var emptyState: some View { VStack(spacing: 14) { Spacer(minLength: 40) TjPlaceholder(label: String(appLoc: "还没有自定义指标")) .frame(width: 220, height: 130) Text("右上角 + 新建一个") .font(.system(size: 12)) .foregroundStyle(Tj.Palette.text3) Spacer() } .frame(maxWidth: .infinity) } private func row(_ m: CustomMonitorMetric) -> some View { let count = usageCount(for: m) return HStack(spacing: 12) { ZStack { Circle().fill(Tj.Palette.leafSoft) Image(systemName: m.icon) .font(.system(size: 17, weight: .medium)) .foregroundStyle(Tj.Palette.ink) } .frame(width: 40, height: 40) VStack(alignment: .leading, spacing: 3) { Text(m.name) .font(.system(size: 15, weight: .semibold)) .foregroundStyle(Tj.Palette.text) .lineLimit(1) HStack(spacing: 6) { if !m.unit.isEmpty { Text(m.unit) .font(.system(size: 11, design: .monospaced)) .foregroundStyle(Tj.Palette.text3) } if !m.rangeText.isEmpty { Text("·") .font(.system(size: 11)) .foregroundStyle(Tj.Palette.text3) Text(m.rangeText) .font(.system(size: 11, design: .monospaced)) .foregroundStyle(Tj.Palette.text3) } } } Spacer(minLength: 8) VStack(alignment: .trailing, spacing: 2) { Text(count == 0 ? String(appLoc: "未使用") : String(appLoc: "用 \(count) 次")) .font(.system(size: 11, weight: count > 0 ? .semibold : .regular)) .foregroundStyle(count > 0 ? Tj.Palette.ink : Tj.Palette.text3) Image(systemName: "chevron.right") .font(.system(size: 11, weight: .medium)) .foregroundStyle(Tj.Palette.text3) } } .padding(14) .background( RoundedRectangle(cornerRadius: Tj.Radius.md, style: .continuous) .fill(Tj.Palette.paper) ) .overlay( RoundedRectangle(cornerRadius: Tj.Radius.md, style: .continuous) .strokeBorder(Tj.Palette.lineSoft, lineWidth: 1) ) } private func usageCount(for m: CustomMonitorMetric) -> Int { indicators.filter { $0.seriesKey == m.seriesKey }.count } } #Preview { NavigationStack { CustomMetricsListView() } .modelContainer(for: [ CustomMonitorMetric.self, Indicator.self, UserProfile.self, MetricReminder.self, ], inMemory: true) }