feat:个性化数据往云端同步

This commit is contained in:
hzm
2026-03-01 16:49:46 +08:00
parent 2a406be0b1
commit e7661e7b38
5 changed files with 187 additions and 25 deletions

View File

@@ -1502,7 +1502,7 @@ export default function HomePage() {
const storageHelper = useMemo(() => {
// 仅以下 key 参与云端同步fundValuationTimeseries 不同步到云端(测试中功能,暂不同步)
const keys = new Set(['funds', 'favorites', 'groups', 'collapsedCodes', 'collapsedTrends', 'refreshMs', 'holdings', 'pendingTrades', 'transactions', 'viewMode', 'dcaPlans']);
const keys = new Set(['funds', 'favorites', 'groups', 'collapsedCodes', 'collapsedTrends', 'refreshMs', 'holdings', 'pendingTrades', 'transactions', 'viewMode', 'dcaPlans', 'customSettings']);
const triggerSync = (key, prevValue, nextValue) => {
if (keys.has(key)) {
// 标记为脏数据
@@ -1549,7 +1549,7 @@ export default function HomePage() {
useEffect(() => {
// 仅以下 key 的变更会触发云端同步fundValuationTimeseries 不在其中
const keys = new Set(['funds', 'favorites', 'groups', 'collapsedCodes', 'collapsedTrends', 'refreshMs', 'holdings', 'pendingTrades', 'viewMode', 'dcaPlans']);
const keys = new Set(['funds', 'favorites', 'groups', 'collapsedCodes', 'collapsedTrends', 'refreshMs', 'holdings', 'pendingTrades', 'viewMode', 'dcaPlans', 'customSettings']);
const onStorage = (e) => {
if (!e.key) return;
if (e.key === 'localUpdatedAt') {
@@ -1570,6 +1570,18 @@ export default function HomePage() {
};
}, [getFundCodesSignature, scheduleSync]);
const triggerCustomSettingsSync = useCallback(() => {
queueMicrotask(() => {
dirtyKeysRef.current.add('customSettings');
if (!skipSyncRef.current) {
const now = nowInTz().toISOString();
window.localStorage.setItem('localUpdatedAt', now);
setLastSyncTime(now);
}
scheduleSync();
});
}, [scheduleSync]);
const applyViewMode = useCallback((mode) => {
if (mode !== 'card' && mode !== 'list') return;
setViewMode(mode);
@@ -2561,6 +2573,7 @@ export default function HomePage() {
const raw = window.localStorage.getItem('customSettings');
const parsed = raw ? JSON.parse(raw) : {};
window.localStorage.setItem('customSettings', JSON.stringify({ ...parsed, pcContainerWidth: w }));
triggerCustomSettingsSync();
} catch { }
setSettingsOpen(false);
};
@@ -2571,6 +2584,7 @@ export default function HomePage() {
const raw = window.localStorage.getItem('customSettings');
const parsed = raw ? JSON.parse(raw) : {};
window.localStorage.setItem('customSettings', JSON.stringify({ ...parsed, pcContainerWidth: 1200 }));
triggerCustomSettingsSync();
} catch { }
};
@@ -2715,6 +2729,7 @@ export default function HomePage() {
});
const viewMode = payload.viewMode === 'list' ? 'list' : 'card';
const customSettings = isPlainObject(payload.customSettings) ? payload.customSettings : {};
return JSON.stringify({
funds: uniqueFundCodes,
@@ -2727,7 +2742,8 @@ export default function HomePage() {
pendingTrades,
transactions,
dcaPlans,
viewMode
viewMode,
customSettings
});
}
@@ -2768,6 +2784,13 @@ export default function HomePage() {
if (!keys || keys.has('dcaPlans')) {
all.dcaPlans = JSON.parse(localStorage.getItem('dcaPlans') || '{}');
}
if (!keys || keys.has('customSettings')) {
try {
all.customSettings = JSON.parse(localStorage.getItem('customSettings') || '{}');
} catch {
all.customSettings = {};
}
}
// 如果是全量收集keys 为 null进行完整的数据清洗和验证逻辑
if (!keys) {
@@ -2837,7 +2860,8 @@ export default function HomePage() {
pendingTrades: all.pendingTrades,
transactions: all.transactions,
dcaPlans: cleanedDcaPlans,
viewMode: all.viewMode
viewMode: all.viewMode,
customSettings: isPlainObject(all.customSettings) ? all.customSettings : {}
};
}
@@ -2858,6 +2882,7 @@ export default function HomePage() {
transactions: {},
dcaPlans: {},
viewMode: 'card',
customSettings: {},
exportedAt: nowInTz().toISOString()
};
}
@@ -2919,6 +2944,13 @@ export default function HomePage() {
setDcaPlans(nextDcaPlans);
storageHelper.setItem('dcaPlans', JSON.stringify(nextDcaPlans));
if (isPlainObject(cloudData.customSettings)) {
try {
const merged = { ...JSON.parse(localStorage.getItem('customSettings') || '{}'), ...cloudData.customSettings };
window.localStorage.setItem('customSettings', JSON.stringify(merged));
} catch { }
}
if (nextFunds.length) {
const codes = Array.from(new Set(nextFunds.map((f) => f.code)));
if (codes.length) await refreshAll(codes);
@@ -3947,6 +3979,7 @@ export default function HomePage() {
if (row.holdingProfitValue == null) return;
setPercentModes(prev => ({ ...prev, [row.code]: !prev[row.code] }));
}}
onCustomSettingsChange={triggerCustomSettingsSync}
/>
</div>
</div>
@@ -3987,6 +4020,7 @@ export default function HomePage() {
if (row.holdingProfitValue == null) return;
setPercentModes((prev) => ({ ...prev, [row.code]: !prev[row.code] }));
}}
onCustomSettingsChange={triggerCustomSettingsSync}
/>
)}
<AnimatePresence mode="popLayout">