+ {headerGroup.headers.map((header, headerIndex) => {
const columnId = header.column.id;
const pinClass = getPinClass(columnId, true);
const alignClass = getAlignClass(columnId);
+ const isLastColumn = headerIndex === headerGroup.headers.length - 1;
return (
{header.isPlaceholder
? null
@@ -536,18 +564,21 @@ export default function MobileFundTable({
background: 'var(--bg)',
position: 'relative',
zIndex: 1,
+ ...(mobileGridLayout.gridTemplateColumns ? { gridTemplateColumns: mobileGridLayout.gridTemplateColumns } : {}),
}}
{...(sortBy === 'default' ? listeners : {})}
>
- {row.getVisibleCells().map((cell) => {
+ {row.getVisibleCells().map((cell, cellIndex) => {
const columnId = cell.column.id;
const pinClass = getPinClass(columnId, false);
const alignClass = getAlignClass(columnId);
const cellClassName = cell.column.columnDef.meta?.cellClassName || '';
+ const isLastColumn = cellIndex === row.getVisibleCells().length - 1;
return (
{flexRender(cell.column.columnDef.cell, cell.getContext())}
diff --git a/app/components/PcFundTable.jsx b/app/components/PcFundTable.jsx
index 9ca3008..e571ce0 100644
--- a/app/components/PcFundTable.jsx
+++ b/app/components/PcFundTable.jsx
@@ -126,6 +126,7 @@ export default function PcFundTable({
refreshing = false,
sortBy = 'default',
onReorder,
+ onCustomSettingsChange,
}) {
const sensors = useSensors(
useSensor(PointerSensor, {
@@ -183,6 +184,7 @@ export default function PcFundTable({
? { ...parsed, pcTableColumns: nextSizing }
: { pcTableColumns: nextSizing };
window.localStorage.setItem('customSettings', JSON.stringify(nextSettings));
+ onCustomSettingsChange?.();
} catch { }
};
@@ -212,6 +214,7 @@ export default function PcFundTable({
? { ...parsed, pcTableColumnOrder: nextOrder }
: { pcTableColumnOrder: nextOrder };
window.localStorage.setItem('customSettings', JSON.stringify(nextSettings));
+ onCustomSettingsChange?.();
} catch { }
};
@@ -246,6 +249,7 @@ export default function PcFundTable({
? { ...parsed, pcTableColumnVisibility: nextVisibility }
: { pcTableColumnVisibility: nextVisibility };
window.localStorage.setItem('customSettings', JSON.stringify(nextSettings));
+ onCustomSettingsChange?.();
} catch { }
};
diff --git a/app/globals.css b/app/globals.css
index f392e00..4069e5d 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -1428,24 +1428,27 @@ input[type="number"] {
}
.mobile-fund-table-scroll {
- min-width: 520px;
+ /* min-width 由 MobileFundTable 根据 columns meta.width 动态设置 */
}
.mobile-fund-table .table-header-row {
display: grid;
- grid-template-columns: 140px 1fr 1fr 1.2fr 1.2fr;
+ /* grid-template-columns 由 MobileFundTable 根据当前列顺序动态设置 */
padding-top: 0 !important;
padding-bottom: 0 !important;
padding-left: 0 !important;
}
.mobile-fund-table .table-header-row .table-header-cell {
- padding-top: 16px;
- padding-bottom: 16px;
+ padding-top: 8px;
+ padding-bottom: 8px;
+ display: flex;
+ align-items: center;
+ min-width: 0;
+ overflow: hidden;
}
.mobile-fund-table .table-row {
- grid-template-columns: 140px 1fr 1fr 1.2fr 1.2fr;
grid-template-areas: unset;
gap: 12px;
padding-top: 0 !important;
@@ -1468,6 +1471,9 @@ input[type="number"] {
.mobile-fund-table .table-row .table-cell {
padding-top: 12px;
padding-bottom: 12px;
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
/* 基金名称列固定左侧:右侧阴影突出固定列,覆盖行 border-bottom */
diff --git a/app/page.jsx b/app/page.jsx
index a573740..a4e4e0b 100644
--- a/app/page.jsx
+++ b/app/page.jsx
@@ -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}
/>