diff --git a/app/globals.css b/app/globals.css index 48885ab..5c6b64b 100644 --- a/app/globals.css +++ b/app/globals.css @@ -709,6 +709,111 @@ input[type="number"] { grid-column: span 12; } +/* PC 列表:左右分块,左侧可横向滚动,右侧操作列固定不参与滚动 */ +.table-pc-wrap { + display: flex; + flex-direction: row; + width: 100%; + min-width: 0; +} + +.table-scroll-area { + flex: 1; + min-width: 0; + overflow-x: auto; +} + +/* 固定像素列宽,避免初始在视口外的列滚入后布局错误、内容不展示;不依赖 fr/内容测量 */ +.table-scroll-area-inner { + width: 1152px; + min-width: 1152px; +} + +/* 基金名称 净值 涨跌幅 估值涨跌幅 估值时间 持仓金额 当日收益 持有收益(当日收益与持有收益同宽) */ +.table-header-row-scroll, +.table-row-scroll { + display: grid; + grid-template-columns: 220px 100px 100px 100px 100px 120px 140px 140px; + align-items: center; + gap: 12px; + width: 100%; + box-sizing: border-box; +} + +.table-header-row-scroll { + padding: 16px 24px; + background: rgba(255, 255, 255, 0.05); + border-bottom: 1px solid var(--border); +} + +.table-header-row-scroll .table-header-cell { + white-space: nowrap; +} + +/* 表格行内容不换行,单行显示 */ +.table-row-scroll .table-cell { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; +} + +.table-row-scroll .name-cell .name-text { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.table-row-scroll { + padding: 12px 24px !important; + min-height: 52px; + box-sizing: border-box; + border-bottom: 1px solid var(--border); + transition: background-color 0.2s ease; +} + +.table-row-scroll:hover, +.table-row-scroll.row-hovered { + background: rgba(255, 255, 255, 0.08); +} + +.table-fixed-row.row-hovered { + background: rgba(255, 255, 255, 0.08); +} + +.table-fixed-col { + width: 60px; + flex-shrink: 0; + display: flex; + flex-direction: column; + background: var(--bg); + border-left: 1px solid var(--border); +} + +.table-header-cell-fixed { + padding: 16px 12px; + min-height: 52px; + display: flex; + align-items: center; + justify-content: center; + border-bottom: 1px solid var(--border); + background: rgba(255, 255, 255, 0.05); +} + +.table-fixed-row { + min-height: 52px; + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: center; + padding: 0 12px; + border-bottom: none; +} + +.table-fixed-row .table-cell { + border: none; +} + .table-row-wrapper { width: 100%; } diff --git a/app/page.jsx b/app/page.jsx index ad5311b..4ce8a17 100644 --- a/app/page.jsx +++ b/app/page.jsx @@ -447,6 +447,7 @@ export default function HomePage() { const todayStr = formatDate(); const [isMobile, setIsMobile] = useState(false); + const [hoveredPcRowCode, setHoveredPcRowCode] = useState(null); // PC 列表行悬浮高亮 const [logoutConfirmOpen, setLogoutConfirmOpen] = useState(false); useEffect(() => { if (typeof window !== 'undefined') { @@ -3638,7 +3639,155 @@ export default function HomePage() { className={viewMode === 'card' ? 'grid' : 'table-container glass'} >
- {viewMode === 'list' && ( + {/* PC 列表:左右分块,左侧 8 列可横向滚动,右侧操作列固定 */} + {viewMode === 'list' && !isMobile && ( +
+
+
+
+
基金名称
+
净值/估值
+
涨跌幅
+
估值涨跌幅
+
估值时间
+
持仓金额
+
当日收益
+
持有收益
+
+ + {displayFunds.map((f) => ( + setHoveredPcRowCode(f.code)} + onMouseLeave={() => setHoveredPcRowCode(null)} + > +
+
+ {currentTab !== 'all' && currentTab !== 'fav' ? ( + + ) : ( + + )} +
+ {f.name} + #{f.code} +
+
+ {(() => { + const hasTodayData = f.jzrq === todayStr; + const shouldHideChange = isTradingDay && !hasTodayData; + const valueDisplay = !shouldHideChange ? (f.dwjz ?? '—') : (f.noValuation ? (f.dwjz ?? '—') : (f.estPricedCoverage > 0.05 ? f.estGsz.toFixed(4) : (f.gsz ?? '—'))); + return ( +
+ {valueDisplay} +
+ ); + })()} +
+ 0 ? 'up' : f.zzl < 0 ? 'down' : ''} style={{ fontWeight: 700 }}> + {f.zzl != null && f.zzl !== '' ? `${f.zzl > 0 ? '+' : ''}${Number(f.zzl).toFixed(2)}%` : '—'} + +
+
+ 0.05 ? (f.estGszzl > 0 ? 'up' : f.estGszzl < 0 ? 'down' : '') : (Number(f.gszzl) > 0 ? 'up' : Number(f.gszzl) < 0 ? 'down' : ''))} style={{ fontWeight: 700 }}> + {f.noValuation ? '—' : (f.estPricedCoverage > 0.05 ? (f.estGszzl != null ? `${f.estGszzl > 0 ? '+' : ''}${Number(f.estGszzl).toFixed(2)}%` : '—') : (isNumber(f.gszzl) ? `${f.gszzl > 0 ? '+' : ''}${Number(f.gszzl).toFixed(2)}%` : (f.gszzl ?? '—')))} + +
+
+ {f.noValuation ? (f.jzrq || '-') : (f.gztime || f.time || '-')} +
+ {(() => { + const holding = holdings[f.code]; + const profit = getHoldingProfit(f, holding); + const amount = profit ? profit.amount : null; + if (amount === null) { + return ( +
{ e.stopPropagation(); setHoldingModal({ open: true, fund: f }); }}> + 未设置 +
+ ); + } + return ( +
{ e.stopPropagation(); setActionModal({ open: true, fund: f }); }}> + ¥{amount.toFixed(2)} + +
+ ); + })()} + {(() => { + const holding = holdings[f.code]; + const profit = getHoldingProfit(f, holding); + const profitValue = profit ? profit.profitToday : null; + const hasProfit = profitValue !== null; + return ( +
+ 0 ? 'up' : profitValue < 0 ? 'down' : '') : 'muted'} style={{ fontWeight: 700 }}> + {hasProfit ? `${profitValue > 0 ? '+' : profitValue < 0 ? '-' : ''}¥${Math.abs(profitValue).toFixed(2)}` : ''} + +
+ ); + })()} + {(() => { + const holding = holdings[f.code]; + const profit = getHoldingProfit(f, holding); + const total = profit ? profit.profitTotal : null; + const principal = holding && holding.cost && holding.share ? holding.cost * holding.share : 0; + const asPercent = percentModes[f.code]; + const hasTotal = total !== null; + const formatted = hasTotal ? (asPercent && principal > 0 ? `${total > 0 ? '+' : total < 0 ? '-' : ''}${Math.abs((total / principal) * 100).toFixed(2)}%` : `${total > 0 ? '+' : total < 0 ? '-' : ''}¥${Math.abs(total).toFixed(2)}`) : ''; + const cls = hasTotal ? (total > 0 ? 'up' : total < 0 ? 'down' : '') : 'muted'; + return ( +
{ e.stopPropagation(); if (hasTotal) setPercentModes(prev => ({ ...prev, [f.code]: !prev[f.code] })); }} style={{ cursor: hasTotal ? 'pointer' : 'default' }}> + {formatted} +
+ ); + })()} +
+
+ ))} +
+
+
+
+
操作
+ + {displayFunds.map((f) => ( + setHoveredPcRowCode(f.code)} + onMouseLeave={() => setHoveredPcRowCode(null)} + > +
+ +
+
+ ))} +
+
+
+ )} + {viewMode === 'list' && isMobile && (
基金名称
净值/估值
@@ -3651,7 +3800,8 @@ export default function HomePage() {
)} - {displayFunds.map((f) => ( + {displayFunds.map((f) => + (viewMode === 'list' && !isMobile) ? null : ( - ))} + ) + )}