feat: 全局设置新增显示大盘指数开关

This commit is contained in:
hzm
2026-03-20 22:17:43 +08:00
parent 9f6d1bb768
commit 270bc3ab08
4 changed files with 90 additions and 30 deletions

View File

@@ -56,12 +56,15 @@ export default function GroupSummary({
groupName,
getProfit,
stickyTop,
isSticky = false,
onToggleSticky,
masked,
onToggleMasked,
marketIndexAccordionHeight,
navbarHeight
}) {
const [showPercent, setShowPercent] = useState(true);
const [isMasked, setIsMasked] = useState(masked ?? false);
const [isSticky, setIsSticky] = useState(false);
const rowRef = useRef(null);
const [assetSize, setAssetSize] = useState(24);
const [metricSize, setMetricSize] = useState(18);
@@ -165,12 +168,22 @@ export default function GroupSummary({
metricSize,
]);
const style = useMemo(()=>{
const style = {};
if (isSticky) {
style.top = stickyTop + 14;
}else if(!marketIndexAccordionHeight) {
style.marginTop = navbarHeight;
}
return style;
},[isSticky, stickyTop, marketIndexAccordionHeight, navbarHeight])
if (!summary.hasHolding) return null;
return (
<div
className={isSticky ? 'group-summary-sticky' : ''}
style={isSticky && stickyTop ? { top: stickyTop } : {}}
style={style}
>
<div
className="glass card group-summary-card"
@@ -183,7 +196,9 @@ export default function GroupSummary({
>
<span
className="sticky-toggle-btn"
onClick={() => setIsSticky(!isSticky)}
onClick={() => {
onToggleSticky?.(!isSticky);
}}
style={{
position: 'absolute',
top: 4,

View File

@@ -349,9 +349,14 @@ export default function MobileFundTable({
if (!stickySummaryWrapper) return stickyTop;
const wrapperRect = stickySummaryWrapper.getBoundingClientRect();
const isSummaryStuck = wrapperRect.top <= stickyTop + 1;
// 用“实际 DOM 的 top”判断 sticky 是否已生效,避免 mobile 下 stickyTop 入参与 GroupSummary 不一致导致的偏移。
const computedTopStr = window.getComputedStyle(stickySummaryWrapper).top;
const computedTop = Number.parseFloat(computedTopStr);
const baseTop = Number.isFinite(computedTop) ? computedTop : stickyTop;
const isSummaryStuck = wrapperRect.top <= baseTop + 1;
return isSummaryStuck ? stickyTop + stickySummaryWrapper.offsetHeight : stickyTop;
// header 使用固定定位(top),所以也用视口坐标系下的 wrapperRect.top + 高度,确保不重叠
return isSummaryStuck ? wrapperRect.top + stickySummaryWrapper.offsetHeight : stickyTop;
};
const updateVerticalState = () => {

View File

@@ -20,13 +20,14 @@ export default function SettingsModal({
containerWidth = 1200,
setContainerWidth,
onResetContainerWidth,
showMarketIndex = true,
setShowMarketIndex,
showMarketIndexPc = true,
showMarketIndexMobile = true,
}) {
const [sliderDragging, setSliderDragging] = useState(false);
const [resetWidthConfirmOpen, setResetWidthConfirmOpen] = useState(false);
const [localSeconds, setLocalSeconds] = useState(tempSeconds);
const [localShowMarketIndex, setLocalShowMarketIndex] = useState(showMarketIndex);
const [localShowMarketIndexPc, setLocalShowMarketIndexPc] = useState(showMarketIndexPc);
const [localShowMarketIndexMobile, setLocalShowMarketIndexMobile] = useState(showMarketIndexMobile);
const pageWidthTrackRef = useRef(null);
const clampedWidth = Math.min(2000, Math.max(600, Number(containerWidth) || 1200));
@@ -60,8 +61,12 @@ export default function SettingsModal({
}, [tempSeconds]);
useEffect(() => {
setLocalShowMarketIndex(showMarketIndex);
}, [showMarketIndex]);
setLocalShowMarketIndexPc(showMarketIndexPc);
}, [showMarketIndexPc]);
useEffect(() => {
setLocalShowMarketIndexMobile(showMarketIndexMobile);
}, [showMarketIndexMobile]);
return (
<Dialog
@@ -170,16 +175,16 @@ export default function SettingsModal({
</div>
)}
<div hidden className="form-group" style={{ marginBottom: 16 }}>
<div className="form-group" style={{ marginBottom: 16 }}>
<div className="muted" style={{ marginBottom: 8, fontSize: '0.8rem' }}>显示大盘指数</div>
<div className="row" style={{ justifyContent: 'flex-start', alignItems: 'center' }}>
<Switch
checked={localShowMarketIndex}
checked={isMobile ? localShowMarketIndexMobile : localShowMarketIndexPc}
className="ml-2 scale-125"
onCheckedChange={(checked) => {
const nextValue = Boolean(checked);
setLocalShowMarketIndex(nextValue);
setShowMarketIndex?.(nextValue);
if (isMobile) setLocalShowMarketIndexMobile(nextValue);
else setLocalShowMarketIndexPc(nextValue);
}}
aria-label="显示大盘指数"
/>
@@ -212,7 +217,12 @@ export default function SettingsModal({
<div className="row" style={{ justifyContent: 'flex-end', marginTop: 24 }}>
<button
className="button"
onClick={(e) => saveSettings(e, localSeconds, localShowMarketIndex)}
onClick={(e) => saveSettings(
e,
localSeconds,
isMobile ? localShowMarketIndexMobile : localShowMarketIndexPc,
isMobile
)}
disabled={localSeconds < 30}
>
保存并关闭

View File

@@ -136,7 +136,9 @@ export default function HomePage() {
const [settingsOpen, setSettingsOpen] = useState(false);
const [tempSeconds, setTempSeconds] = useState(60);
const [containerWidth, setContainerWidth] = useState(1200);
const [showMarketIndex, setShowMarketIndex] = useState(true);
const [showMarketIndexPc, setShowMarketIndexPc] = useState(true);
const [showMarketIndexMobile, setShowMarketIndexMobile] = useState(true);
const [isGroupSummarySticky, setIsGroupSummarySticky] = useState(false);
useEffect(() => {
if (typeof window === 'undefined') return;
@@ -149,9 +151,8 @@ export default function HomePage() {
if (Number.isFinite(num)) {
setContainerWidth(Math.min(2000, Math.max(600, num)));
}
if (typeof parsed?.showMarketIndex === 'boolean') {
setShowMarketIndex(parsed.showMarketIndex);
}
if (typeof parsed?.showMarketIndexPc === 'boolean') setShowMarketIndexPc(parsed.showMarketIndexPc);
if (typeof parsed?.showMarketIndexMobile === 'boolean') setShowMarketIndexMobile(parsed.showMarketIndexMobile);
} catch { }
}, []);
@@ -417,6 +418,7 @@ export default function HomePage() {
clearTimeout(timer);
};
}, [groups, currentTab]); // groups 或 tab 变化可能导致 filterBar 高度变化
const handleMobileSearchClick = (e) => {
e?.preventDefault();
e?.stopPropagation();
@@ -469,6 +471,13 @@ export default function HomePage() {
}
}, []);
const shouldShowMarketIndex = isMobile ? showMarketIndexMobile : showMarketIndexPc;
// 当关闭大盘指数时,重置它的高度,避免 top/stickyTop 仍沿用旧值
useEffect(() => {
if (!shouldShowMarketIndex) setMarketIndexAccordionHeight(0);
}, [shouldShowMarketIndex]);
// 检查更新
const [hasUpdate, setHasUpdate] = useState(false);
const [latestVersion, setLatestVersion] = useState('');
@@ -2806,25 +2815,41 @@ export default function HomePage() {
await refreshAll(codes);
};
const saveSettings = (e, secondsOverride, showMarketIndexOverride) => {
const saveSettings = (e, secondsOverride, showMarketIndexOverride, isMobileOverride) => {
e?.preventDefault?.();
const seconds = secondsOverride ?? tempSeconds;
const shouldShowMarketIndex = typeof showMarketIndexOverride === 'boolean' ? showMarketIndexOverride : showMarketIndex;
const ms = Math.max(30, Number(seconds)) * 1000;
setTempSeconds(Math.round(ms / 1000));
setRefreshMs(ms);
setShowMarketIndex(shouldShowMarketIndex);
const nextShowMarketIndex = typeof showMarketIndexOverride === 'boolean'
? showMarketIndexOverride
: isMobileOverride
? showMarketIndexMobile
: showMarketIndexPc;
const targetIsMobile = Boolean(isMobileOverride);
if (targetIsMobile) setShowMarketIndexMobile(nextShowMarketIndex);
else setShowMarketIndexPc(nextShowMarketIndex);
storageHelper.setItem('refreshMs', String(ms));
const w = Math.min(2000, Math.max(600, Number(containerWidth) || 1200));
setContainerWidth(w);
try {
const raw = window.localStorage.getItem('customSettings');
const parsed = raw ? JSON.parse(raw) : {};
if (targetIsMobile) {
// 仅更新当前运行端对应的开关键
window.localStorage.setItem('customSettings', JSON.stringify({
...parsed,
pcContainerWidth: w,
showMarketIndex: shouldShowMarketIndex,
showMarketIndexMobile: nextShowMarketIndex,
}));
} else {
window.localStorage.setItem('customSettings', JSON.stringify({
...parsed,
pcContainerWidth: w,
showMarketIndexPc: nextShowMarketIndex,
}));
}
triggerCustomSettingsSync();
} catch { }
setSettingsOpen(false);
@@ -3982,7 +4007,7 @@ export default function HomePage() {
</div>
</div>
</div>
{showMarketIndex && (
{shouldShowMarketIndex && (
<MarketIndexAccordion
navbarHeight={navbarHeight}
onHeightChange={setMarketIndexAccordionHeight}
@@ -4207,8 +4232,12 @@ export default function HomePage() {
groupName={getGroupName()}
getProfit={getHoldingProfit}
stickyTop={navbarHeight + marketIndexAccordionHeight + filterBarHeight + (isMobile ? -14 : 0)}
isSticky={isGroupSummarySticky}
onToggleSticky={(next) => setIsGroupSummarySticky(next)}
masked={maskAmounts}
onToggleMasked={() => setMaskAmounts((v) => !v)}
marketIndexAccordionHeight={marketIndexAccordionHeight}
navbarHeight={navbarHeight}
/>
{currentTab !== 'all' && currentTab !== 'fav' && (
@@ -4258,6 +4287,7 @@ export default function HomePage() {
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.2 }}
className={viewMode === 'card' ? 'grid' : 'table-container glass'}
style={{ marginTop: isGroupSummarySticky ? 50 : 0 }}
>
<div className={viewMode === 'card' ? 'grid col-12' : ''} style={viewMode === 'card' ? { gridColumn: 'span 12', gap: 16 } : {}}>
{/* PC 列表:使用 PcFundTable + 右侧冻结操作列 */}
@@ -4810,8 +4840,8 @@ export default function HomePage() {
containerWidth={containerWidth}
setContainerWidth={setContainerWidth}
onResetContainerWidth={handleResetContainerWidth}
showMarketIndex={showMarketIndex}
setShowMarketIndex={setShowMarketIndex}
showMarketIndexPc={showMarketIndexPc}
showMarketIndexMobile={showMarketIndexMobile}
/>
)}