Files
vgearenandClaude Opus 4.6 592ad90f61 v1.2.5: Add watchlist (观察仓) feature with virtual portfolio support
- New Watchlist model: named virtual portfolios with per-stock cost/shares
- Watchlists persisted to watchlists.json, active ID in settings
- AppState: effective cost/shares/PnL methods that respect active watchlist
- Settings: create/delete/switch watchlists, edit writes to active watchlist
- ProfitSummaryView shows active watchlist name
- UI alignment fixes for toggle and text field padding

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 11:31:59 +08:00

83 lines
3.7 KiB
Swift

import SwiftUI
struct StockRowView: View {
let stock: Stock
let quote: Quote?
var onTap: (() -> Void)? = nil
@EnvironmentObject var appState: AppState
var body: some View {
HStack(alignment: .center, spacing: 0) {
// 左侧:名称 + 代码
VStack(alignment: .leading, spacing: 1) {
Text(stock.name)
.font(.system(size: 12, weight: .medium))
.lineLimit(1)
Text(stock.id)
.font(.system(size: 9))
.foregroundColor(.secondary)
}
Spacer(minLength: 4)
// 右侧:行情 + 盈亏
if let q = quote {
VStack(alignment: .trailing, spacing: 1) {
HStack(spacing: 4) {
Text(q.formattedPrice)
.font(.system(size: 12, weight: .medium))
.foregroundColor(appState.quoteColor(for: q))
Text(q.formattedPercent)
.font(.system(size: 11))
.foregroundColor(appState.quoteColor(for: q))
}
if stock.market == .usStock,
let session = AppState.usMarketSession(),
let extPrice = q.formattedExtendedPrice,
let extPct = q.formattedExtendedPercent {
Text("\(session) \(extPrice) \(extPct)")
.font(.system(size: 9))
.foregroundColor(
(q.extendedChangePercent ?? 0) >= 0
? Color(appState.config.upColorName)
: Color(appState.config.downColorName)
)
}
if appState.effectiveShares(for: stock) == nil {
Text("未持仓")
.font(.system(size: 9))
.foregroundColor(.secondary)
} else {
HStack(spacing: 4) {
if let daily = appState.effectiveDailyPnl(stock: stock, quote: q) {
Text(daily >= 0
? "日+\(String(format: "%.2f", daily))"
: "日\(String(format: "%.2f", daily))")
.font(.system(size: 10))
.foregroundColor(appState.pnlColor(daily))
}
if let pnl = appState.effectivePnl(stock: stock, quote: q) {
let pct = appState.effectivePnlPercent(stock: stock, quote: q)
let pctStr = pct.map { String(format: " %.2f%%", $0) } ?? ""
Text(pnl >= 0
? "浮+\(String(format: "%.2f", pnl))\(pctStr)"
: "浮\(String(format: "%.2f", pnl))\(pctStr)")
.font(.system(size: 10))
.foregroundColor(appState.pnlColor(pnl))
}
}
}
}
} else {
Text("--")
.foregroundColor(.secondary)
.font(.system(size: 12))
}
}
.padding(.horizontal, 6)
.padding(.vertical, 3)
.background(Color.primary.opacity(0.04))
.cornerRadius(4)
.contentShape(Rectangle())
.onTapGesture { onTap?() }
}
}