feat: 全局设置新增显示大盘指数开关
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
保存并关闭
|
||||
|
||||
60
app/page.jsx
60
app/page.jsx
@@ -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) : {};
|
||||
window.localStorage.setItem('customSettings', JSON.stringify({
|
||||
...parsed,
|
||||
pcContainerWidth: w,
|
||||
showMarketIndex: shouldShowMarketIndex,
|
||||
}));
|
||||
if (targetIsMobile) {
|
||||
// 仅更新当前运行端对应的开关键
|
||||
window.localStorage.setItem('customSettings', JSON.stringify({
|
||||
...parsed,
|
||||
pcContainerWidth: w,
|
||||
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}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user