feat:分组下个性化数据独立
This commit is contained in:
@@ -141,78 +141,112 @@ export default function MobileFundTable({
|
|||||||
setActiveId(null);
|
setActiveId(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStoredMobileColumnOrder = () => {
|
const groupKey = currentTab ?? 'all';
|
||||||
if (typeof window === 'undefined') return null;
|
|
||||||
|
const getCustomSettingsWithMigration = () => {
|
||||||
|
if (typeof window === 'undefined') return {};
|
||||||
try {
|
try {
|
||||||
const raw = window.localStorage.getItem('customSettings');
|
const raw = window.localStorage.getItem('customSettings');
|
||||||
if (!raw) return null;
|
const parsed = raw ? JSON.parse(raw) : {};
|
||||||
const parsed = JSON.parse(raw);
|
if (!parsed || typeof parsed !== 'object') return {};
|
||||||
const order = parsed?.mobileTableColumnOrder;
|
if (parsed.pcTableColumnOrder != null || parsed.pcTableColumnVisibility != null || parsed.pcTableColumns != null || parsed.mobileTableColumnOrder != null || parsed.mobileTableColumnVisibility != null) {
|
||||||
if (!Array.isArray(order) || order.length === 0) return null;
|
const all = {
|
||||||
const valid = order.filter((id) => MOBILE_NON_FROZEN_COLUMN_IDS.includes(id));
|
...(parsed.all && typeof parsed.all === 'object' ? parsed.all : {}),
|
||||||
const missing = MOBILE_NON_FROZEN_COLUMN_IDS.filter((id) => !valid.includes(id));
|
pcTableColumnOrder: parsed.pcTableColumnOrder,
|
||||||
return [...valid, ...missing];
|
pcTableColumnVisibility: parsed.pcTableColumnVisibility,
|
||||||
|
pcTableColumns: parsed.pcTableColumns,
|
||||||
|
mobileTableColumnOrder: parsed.mobileTableColumnOrder,
|
||||||
|
mobileTableColumnVisibility: parsed.mobileTableColumnVisibility,
|
||||||
|
};
|
||||||
|
delete parsed.pcTableColumnOrder;
|
||||||
|
delete parsed.pcTableColumnVisibility;
|
||||||
|
delete parsed.pcTableColumns;
|
||||||
|
delete parsed.mobileTableColumnOrder;
|
||||||
|
delete parsed.mobileTableColumnVisibility;
|
||||||
|
parsed.all = all;
|
||||||
|
window.localStorage.setItem('customSettings', JSON.stringify(parsed));
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return {};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const persistMobileColumnOrder = (nextOrder) => {
|
|
||||||
|
const getInitialMobileConfigByGroup = () => {
|
||||||
|
const parsed = getCustomSettingsWithMigration();
|
||||||
|
const byGroup = {};
|
||||||
|
Object.keys(parsed).forEach((k) => {
|
||||||
|
if (k === 'pcContainerWidth') return;
|
||||||
|
const group = parsed[k];
|
||||||
|
if (!group || typeof group !== 'object') return;
|
||||||
|
const order = Array.isArray(group.mobileTableColumnOrder) && group.mobileTableColumnOrder.length > 0
|
||||||
|
? group.mobileTableColumnOrder
|
||||||
|
: null;
|
||||||
|
const visibility = group.mobileTableColumnVisibility && typeof group.mobileTableColumnVisibility === 'object'
|
||||||
|
? group.mobileTableColumnVisibility
|
||||||
|
: null;
|
||||||
|
byGroup[k] = {
|
||||||
|
mobileTableColumnOrder: order ? (() => {
|
||||||
|
const valid = order.filter((id) => MOBILE_NON_FROZEN_COLUMN_IDS.includes(id));
|
||||||
|
const missing = MOBILE_NON_FROZEN_COLUMN_IDS.filter((id) => !valid.includes(id));
|
||||||
|
return [...valid, ...missing];
|
||||||
|
})() : null,
|
||||||
|
mobileTableColumnVisibility: visibility,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return byGroup;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [configByGroup, setConfigByGroup] = useState(getInitialMobileConfigByGroup);
|
||||||
|
|
||||||
|
const currentGroupMobile = configByGroup[groupKey];
|
||||||
|
const defaultOrder = [...MOBILE_NON_FROZEN_COLUMN_IDS];
|
||||||
|
const defaultVisibility = (() => {
|
||||||
|
const o = {};
|
||||||
|
MOBILE_NON_FROZEN_COLUMN_IDS.forEach((id) => { o[id] = true; });
|
||||||
|
return o;
|
||||||
|
})();
|
||||||
|
|
||||||
|
const mobileColumnOrder = (() => {
|
||||||
|
const order = currentGroupMobile?.mobileTableColumnOrder ?? defaultOrder;
|
||||||
|
if (!Array.isArray(order) || order.length === 0) return [...MOBILE_NON_FROZEN_COLUMN_IDS];
|
||||||
|
const valid = order.filter((id) => MOBILE_NON_FROZEN_COLUMN_IDS.includes(id));
|
||||||
|
const missing = MOBILE_NON_FROZEN_COLUMN_IDS.filter((id) => !valid.includes(id));
|
||||||
|
return [...valid, ...missing];
|
||||||
|
})();
|
||||||
|
const mobileColumnVisibility = (() => {
|
||||||
|
const vis = currentGroupMobile?.mobileTableColumnVisibility ?? null;
|
||||||
|
if (vis && typeof vis === 'object' && Object.keys(vis).length > 0) return vis;
|
||||||
|
return defaultVisibility;
|
||||||
|
})();
|
||||||
|
|
||||||
|
const persistMobileGroupConfig = (updates) => {
|
||||||
if (typeof window === 'undefined') return;
|
if (typeof window === 'undefined') return;
|
||||||
try {
|
try {
|
||||||
const raw = window.localStorage.getItem('customSettings');
|
const raw = window.localStorage.getItem('customSettings');
|
||||||
const parsed = raw ? JSON.parse(raw) : {};
|
const parsed = raw ? JSON.parse(raw) : {};
|
||||||
const nextSettings =
|
const group = parsed[groupKey] && typeof parsed[groupKey] === 'object' ? { ...parsed[groupKey] } : {};
|
||||||
parsed && typeof parsed === 'object'
|
if (updates.mobileTableColumnOrder !== undefined) group.mobileTableColumnOrder = updates.mobileTableColumnOrder;
|
||||||
? { ...parsed, mobileTableColumnOrder: nextOrder }
|
if (updates.mobileTableColumnVisibility !== undefined) group.mobileTableColumnVisibility = updates.mobileTableColumnVisibility;
|
||||||
: { mobileTableColumnOrder: nextOrder };
|
parsed[groupKey] = group;
|
||||||
window.localStorage.setItem('customSettings', JSON.stringify(nextSettings));
|
window.localStorage.setItem('customSettings', JSON.stringify(parsed));
|
||||||
onCustomSettingsChange?.();
|
setConfigByGroup((prev) => ({ ...prev, [groupKey]: { ...prev[groupKey], ...updates } }));
|
||||||
} catch {}
|
|
||||||
};
|
|
||||||
const getStoredMobileColumnVisibility = () => {
|
|
||||||
if (typeof window === 'undefined') return null;
|
|
||||||
try {
|
|
||||||
const raw = window.localStorage.getItem('customSettings');
|
|
||||||
if (!raw) return null;
|
|
||||||
const parsed = JSON.parse(raw);
|
|
||||||
const visibility = parsed?.mobileTableColumnVisibility;
|
|
||||||
if (!visibility || typeof visibility !== 'object') return null;
|
|
||||||
const normalized = {};
|
|
||||||
MOBILE_NON_FROZEN_COLUMN_IDS.forEach((id) => {
|
|
||||||
const value = visibility[id];
|
|
||||||
if (typeof value === 'boolean') normalized[id] = value;
|
|
||||||
});
|
|
||||||
return Object.keys(normalized).length ? normalized : null;
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const persistMobileColumnVisibility = (nextVisibility) => {
|
|
||||||
if (typeof window === 'undefined') return;
|
|
||||||
try {
|
|
||||||
const raw = window.localStorage.getItem('customSettings');
|
|
||||||
const parsed = raw ? JSON.parse(raw) : {};
|
|
||||||
const nextSettings =
|
|
||||||
parsed && typeof parsed === 'object'
|
|
||||||
? { ...parsed, mobileTableColumnVisibility: nextVisibility }
|
|
||||||
: { mobileTableColumnVisibility: nextVisibility };
|
|
||||||
window.localStorage.setItem('customSettings', JSON.stringify(nextSettings));
|
|
||||||
onCustomSettingsChange?.();
|
onCustomSettingsChange?.();
|
||||||
} catch {}
|
} catch {}
|
||||||
};
|
};
|
||||||
|
|
||||||
const [mobileColumnOrder, setMobileColumnOrder] = useState(
|
const setMobileColumnOrder = (nextOrderOrUpdater) => {
|
||||||
() => getStoredMobileColumnOrder() ?? [...MOBILE_NON_FROZEN_COLUMN_IDS]
|
const next = typeof nextOrderOrUpdater === 'function'
|
||||||
);
|
? nextOrderOrUpdater(mobileColumnOrder)
|
||||||
const [mobileColumnVisibility, setMobileColumnVisibility] = useState(() => {
|
: nextOrderOrUpdater;
|
||||||
const stored = getStoredMobileColumnVisibility();
|
persistMobileGroupConfig({ mobileTableColumnOrder: next });
|
||||||
if (stored) return stored;
|
};
|
||||||
const allVisible = {};
|
const setMobileColumnVisibility = (nextOrUpdater) => {
|
||||||
MOBILE_NON_FROZEN_COLUMN_IDS.forEach((id) => {
|
const next = typeof nextOrUpdater === 'function'
|
||||||
allVisible[id] = true;
|
? nextOrUpdater(mobileColumnVisibility)
|
||||||
});
|
: nextOrUpdater;
|
||||||
return allVisible;
|
persistMobileGroupConfig({ mobileTableColumnVisibility: next });
|
||||||
});
|
};
|
||||||
const [settingModalOpen, setSettingModalOpen] = useState(false);
|
const [settingModalOpen, setSettingModalOpen] = useState(false);
|
||||||
const tableContainerRef = useRef(null);
|
const tableContainerRef = useRef(null);
|
||||||
const [tableContainerWidth, setTableContainerWidth] = useState(0);
|
const [tableContainerWidth, setTableContainerWidth] = useState(0);
|
||||||
@@ -258,9 +292,7 @@ export default function MobileFundTable({
|
|||||||
}, [tableContainerWidth, mobileColumnOrder, mobileColumnVisibility]);
|
}, [tableContainerWidth, mobileColumnOrder, mobileColumnVisibility]);
|
||||||
|
|
||||||
const handleResetMobileColumnOrder = () => {
|
const handleResetMobileColumnOrder = () => {
|
||||||
const defaultOrder = [...MOBILE_NON_FROZEN_COLUMN_IDS];
|
setMobileColumnOrder([...MOBILE_NON_FROZEN_COLUMN_IDS]);
|
||||||
setMobileColumnOrder(defaultOrder);
|
|
||||||
persistMobileColumnOrder(defaultOrder);
|
|
||||||
};
|
};
|
||||||
const handleResetMobileColumnVisibility = () => {
|
const handleResetMobileColumnVisibility = () => {
|
||||||
const allVisible = {};
|
const allVisible = {};
|
||||||
@@ -268,14 +300,9 @@ export default function MobileFundTable({
|
|||||||
allVisible[id] = true;
|
allVisible[id] = true;
|
||||||
});
|
});
|
||||||
setMobileColumnVisibility(allVisible);
|
setMobileColumnVisibility(allVisible);
|
||||||
persistMobileColumnVisibility(allVisible);
|
|
||||||
};
|
};
|
||||||
const handleToggleMobileColumnVisibility = (columnId, visible) => {
|
const handleToggleMobileColumnVisibility = (columnId, visible) => {
|
||||||
setMobileColumnVisibility((prev = {}) => {
|
setMobileColumnVisibility((prev = {}) => ({ ...prev, [columnId]: visible }));
|
||||||
const next = { ...prev, [columnId]: visible };
|
|
||||||
persistMobileColumnVisibility(next);
|
|
||||||
return next;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 移动端名称列:无拖拽把手,长按整行触发排序
|
// 移动端名称列:无拖拽把手,长按整行触发排序
|
||||||
@@ -298,6 +325,7 @@ export default function MobileFundTable({
|
|||||||
onRemoveFromGroupRef.current?.(original);
|
onRemoveFromGroupRef.current?.(original);
|
||||||
}}
|
}}
|
||||||
title="从当前分组移除"
|
title="从当前分组移除"
|
||||||
|
style={{ backgroundColor: 'transparent'}}
|
||||||
>
|
>
|
||||||
<ExitIcon width="18" height="18" style={{ transform: 'rotate(180deg)' }} />
|
<ExitIcon width="18" height="18" style={{ transform: 'rotate(180deg)' }} />
|
||||||
</button>
|
</button>
|
||||||
@@ -309,6 +337,7 @@ export default function MobileFundTable({
|
|||||||
onToggleFavoriteRef.current?.(original);
|
onToggleFavoriteRef.current?.(original);
|
||||||
}}
|
}}
|
||||||
title={isFavorites ? '取消自选' : '添加自选'}
|
title={isFavorites ? '取消自选' : '添加自选'}
|
||||||
|
style={{ backgroundColor: 'transparent'}}
|
||||||
>
|
>
|
||||||
<StarIcon width="18" height="18" filled={isFavorites} />
|
<StarIcon width="18" height="18" filled={isFavorites} />
|
||||||
</button>
|
</button>
|
||||||
@@ -538,7 +567,6 @@ export default function MobileFundTable({
|
|||||||
const newNonFrozen = next.filter((id) => id !== 'fundName');
|
const newNonFrozen = next.filter((id) => id !== 'fundName');
|
||||||
if (newNonFrozen.length) {
|
if (newNonFrozen.length) {
|
||||||
setMobileColumnOrder(newNonFrozen);
|
setMobileColumnOrder(newNonFrozen);
|
||||||
persistMobileColumnOrder(newNonFrozen);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onColumnVisibilityChange: (updater) => {
|
onColumnVisibilityChange: (updater) => {
|
||||||
@@ -546,7 +574,6 @@ export default function MobileFundTable({
|
|||||||
const rest = { ...next };
|
const rest = { ...next };
|
||||||
delete rest.fundName;
|
delete rest.fundName;
|
||||||
setMobileColumnVisibility(rest);
|
setMobileColumnVisibility(rest);
|
||||||
persistMobileColumnVisibility(rest);
|
|
||||||
},
|
},
|
||||||
initialState: {
|
initialState: {
|
||||||
columnPinning: {
|
columnPinning: {
|
||||||
@@ -738,7 +765,6 @@ export default function MobileFundTable({
|
|||||||
columnVisibility={mobileColumnVisibility}
|
columnVisibility={mobileColumnVisibility}
|
||||||
onColumnReorder={(newOrder) => {
|
onColumnReorder={(newOrder) => {
|
||||||
setMobileColumnOrder(newOrder);
|
setMobileColumnOrder(newOrder);
|
||||||
persistMobileColumnOrder(newOrder);
|
|
||||||
}}
|
}}
|
||||||
onToggleColumnVisibility={handleToggleMobileColumnVisibility}
|
onToggleColumnVisibility={handleToggleMobileColumnVisibility}
|
||||||
onResetColumnOrder={handleResetMobileColumnOrder}
|
onResetColumnOrder={handleResetMobileColumnOrder}
|
||||||
|
|||||||
@@ -161,131 +161,160 @@ export default function PcFundTable({
|
|||||||
}
|
}
|
||||||
setActiveId(null);
|
setActiveId(null);
|
||||||
};
|
};
|
||||||
const getStoredColumnSizing = () => {
|
const groupKey = currentTab ?? 'all';
|
||||||
|
|
||||||
|
const getCustomSettingsWithMigration = () => {
|
||||||
if (typeof window === 'undefined') return {};
|
if (typeof window === 'undefined') return {};
|
||||||
try {
|
try {
|
||||||
const raw = window.localStorage.getItem('customSettings');
|
const raw = window.localStorage.getItem('customSettings');
|
||||||
if (!raw) return {};
|
const parsed = raw ? JSON.parse(raw) : {};
|
||||||
const parsed = JSON.parse(raw);
|
if (!parsed || typeof parsed !== 'object') return {};
|
||||||
const sizing = parsed?.pcTableColumns;
|
if (parsed.pcTableColumnOrder != null || parsed.pcTableColumnVisibility != null || parsed.pcTableColumns != null || parsed.mobileTableColumnOrder != null || parsed.mobileTableColumnVisibility != null) {
|
||||||
if (!sizing || typeof sizing !== 'object') return {};
|
const all = {
|
||||||
return Object.fromEntries(
|
...(parsed.all && typeof parsed.all === 'object' ? parsed.all : {}),
|
||||||
Object.entries(sizing).filter(([, value]) => Number.isFinite(value)),
|
pcTableColumnOrder: parsed.pcTableColumnOrder,
|
||||||
);
|
pcTableColumnVisibility: parsed.pcTableColumnVisibility,
|
||||||
|
pcTableColumns: parsed.pcTableColumns,
|
||||||
|
mobileTableColumnOrder: parsed.mobileTableColumnOrder,
|
||||||
|
mobileTableColumnVisibility: parsed.mobileTableColumnVisibility,
|
||||||
|
};
|
||||||
|
delete parsed.pcTableColumnOrder;
|
||||||
|
delete parsed.pcTableColumnVisibility;
|
||||||
|
delete parsed.pcTableColumns;
|
||||||
|
delete parsed.mobileTableColumnOrder;
|
||||||
|
delete parsed.mobileTableColumnVisibility;
|
||||||
|
parsed.all = all;
|
||||||
|
window.localStorage.setItem('customSettings', JSON.stringify(parsed));
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
} catch {
|
} catch {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const persistColumnSizing = (nextSizing) => {
|
const buildPcConfigFromGroup = (group) => {
|
||||||
if (typeof window === 'undefined') return;
|
if (!group || typeof group !== 'object') return null;
|
||||||
try {
|
const sizing = group.pcTableColumns;
|
||||||
const raw = window.localStorage.getItem('customSettings');
|
const sizingObj = sizing && typeof sizing === 'object'
|
||||||
const parsed = raw ? JSON.parse(raw) : {};
|
? Object.fromEntries(Object.entries(sizing).filter(([, v]) => Number.isFinite(v)))
|
||||||
const nextSettings =
|
: {};
|
||||||
parsed && typeof parsed === 'object'
|
if (sizingObj.actions) {
|
||||||
? { ...parsed, pcTableColumns: nextSizing }
|
const { actions, ...rest } = sizingObj;
|
||||||
: { pcTableColumns: nextSizing };
|
Object.assign(sizingObj, rest);
|
||||||
window.localStorage.setItem('customSettings', JSON.stringify(nextSettings));
|
delete sizingObj.actions;
|
||||||
onCustomSettingsChange?.();
|
|
||||||
} catch { }
|
|
||||||
};
|
|
||||||
|
|
||||||
const getStoredColumnOrder = () => {
|
|
||||||
if (typeof window === 'undefined') return null;
|
|
||||||
try {
|
|
||||||
const raw = window.localStorage.getItem('customSettings');
|
|
||||||
if (!raw) return null;
|
|
||||||
const parsed = JSON.parse(raw);
|
|
||||||
const order = parsed?.pcTableColumnOrder;
|
|
||||||
if (!Array.isArray(order) || order.length === 0) return null;
|
|
||||||
const valid = order.filter((id) => NON_FROZEN_COLUMN_IDS.includes(id));
|
|
||||||
const missing = NON_FROZEN_COLUMN_IDS.filter((id) => !valid.includes(id));
|
|
||||||
return [...valid, ...missing];
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
const order = Array.isArray(group.pcTableColumnOrder) && group.pcTableColumnOrder.length > 0
|
||||||
|
? group.pcTableColumnOrder
|
||||||
|
: null;
|
||||||
|
const visibility = group.pcTableColumnVisibility && typeof group.pcTableColumnVisibility === 'object'
|
||||||
|
? group.pcTableColumnVisibility
|
||||||
|
: null;
|
||||||
|
return { sizing: sizingObj, order, visibility };
|
||||||
};
|
};
|
||||||
|
|
||||||
const persistColumnOrder = (nextOrder) => {
|
const getDefaultPcGroupConfig = () => ({
|
||||||
if (typeof window === 'undefined') return;
|
order: [...NON_FROZEN_COLUMN_IDS],
|
||||||
try {
|
visibility: null,
|
||||||
const raw = window.localStorage.getItem('customSettings');
|
sizing: {},
|
||||||
const parsed = raw ? JSON.parse(raw) : {};
|
|
||||||
const nextSettings =
|
|
||||||
parsed && typeof parsed === 'object'
|
|
||||||
? { ...parsed, pcTableColumnOrder: nextOrder }
|
|
||||||
: { pcTableColumnOrder: nextOrder };
|
|
||||||
window.localStorage.setItem('customSettings', JSON.stringify(nextSettings));
|
|
||||||
onCustomSettingsChange?.();
|
|
||||||
} catch { }
|
|
||||||
};
|
|
||||||
|
|
||||||
const getStoredColumnVisibility = () => {
|
|
||||||
if (typeof window === 'undefined') return null;
|
|
||||||
try {
|
|
||||||
const raw = window.localStorage.getItem('customSettings');
|
|
||||||
if (!raw) return null;
|
|
||||||
const parsed = JSON.parse(raw);
|
|
||||||
const visibility = parsed?.pcTableColumnVisibility;
|
|
||||||
if (!visibility || typeof visibility !== 'object') return null;
|
|
||||||
const normalized = {};
|
|
||||||
NON_FROZEN_COLUMN_IDS.forEach((id) => {
|
|
||||||
const value = visibility[id];
|
|
||||||
if (typeof value === 'boolean') {
|
|
||||||
normalized[id] = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return Object.keys(normalized).length ? normalized : null;
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const persistColumnVisibility = (nextVisibility) => {
|
|
||||||
if (typeof window === 'undefined') return;
|
|
||||||
try {
|
|
||||||
const raw = window.localStorage.getItem('customSettings');
|
|
||||||
const parsed = raw ? JSON.parse(raw) : {};
|
|
||||||
const nextSettings =
|
|
||||||
parsed && typeof parsed === 'object'
|
|
||||||
? { ...parsed, pcTableColumnVisibility: nextVisibility }
|
|
||||||
: { pcTableColumnVisibility: nextVisibility };
|
|
||||||
window.localStorage.setItem('customSettings', JSON.stringify(nextSettings));
|
|
||||||
onCustomSettingsChange?.();
|
|
||||||
} catch { }
|
|
||||||
};
|
|
||||||
|
|
||||||
const [columnSizing, setColumnSizing] = useState(() => {
|
|
||||||
const stored = getStoredColumnSizing();
|
|
||||||
if (stored.actions) {
|
|
||||||
const { actions, ...rest } = stored;
|
|
||||||
return rest;
|
|
||||||
}
|
|
||||||
return stored;
|
|
||||||
});
|
});
|
||||||
const [columnOrder, setColumnOrder] = useState(() => getStoredColumnOrder() ?? [...NON_FROZEN_COLUMN_IDS]);
|
|
||||||
const [columnVisibility, setColumnVisibility] = useState(() => {
|
const getInitialConfigByGroup = () => {
|
||||||
const stored = getStoredColumnVisibility();
|
const parsed = getCustomSettingsWithMigration();
|
||||||
if (stored) return stored;
|
const byGroup = {};
|
||||||
const allVisible = {};
|
Object.keys(parsed).forEach((k) => {
|
||||||
NON_FROZEN_COLUMN_IDS.forEach((id) => {
|
if (k === 'pcContainerWidth') return;
|
||||||
allVisible[id] = true;
|
const group = parsed[k];
|
||||||
|
const pc = buildPcConfigFromGroup(group);
|
||||||
|
if (pc) {
|
||||||
|
byGroup[k] = {
|
||||||
|
pcTableColumnOrder: pc.order ? (() => {
|
||||||
|
const valid = pc.order.filter((id) => NON_FROZEN_COLUMN_IDS.includes(id));
|
||||||
|
const missing = NON_FROZEN_COLUMN_IDS.filter((id) => !valid.includes(id));
|
||||||
|
return [...valid, ...missing];
|
||||||
|
})() : null,
|
||||||
|
pcTableColumnVisibility: pc.visibility,
|
||||||
|
pcTableColumns: Object.keys(pc.sizing).length ? pc.sizing : null,
|
||||||
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
return byGroup;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [configByGroup, setConfigByGroup] = useState(getInitialConfigByGroup);
|
||||||
|
|
||||||
|
const currentGroupPc = configByGroup[groupKey];
|
||||||
|
const defaultPc = getDefaultPcGroupConfig();
|
||||||
|
const columnOrder = (() => {
|
||||||
|
const order = currentGroupPc?.pcTableColumnOrder ?? defaultPc.order;
|
||||||
|
if (!Array.isArray(order) || order.length === 0) return [...NON_FROZEN_COLUMN_IDS];
|
||||||
|
const valid = order.filter((id) => NON_FROZEN_COLUMN_IDS.includes(id));
|
||||||
|
const missing = NON_FROZEN_COLUMN_IDS.filter((id) => !valid.includes(id));
|
||||||
|
return [...valid, ...missing];
|
||||||
|
})();
|
||||||
|
const columnVisibility = (() => {
|
||||||
|
const vis = currentGroupPc?.pcTableColumnVisibility ?? null;
|
||||||
|
if (vis && typeof vis === 'object' && Object.keys(vis).length > 0) return vis;
|
||||||
|
const allVisible = {};
|
||||||
|
NON_FROZEN_COLUMN_IDS.forEach((id) => { allVisible[id] = true; });
|
||||||
return allVisible;
|
return allVisible;
|
||||||
});
|
})();
|
||||||
|
const columnSizing = (() => {
|
||||||
|
const s = currentGroupPc?.pcTableColumns;
|
||||||
|
if (s && typeof s === 'object') {
|
||||||
|
const out = Object.fromEntries(Object.entries(s).filter(([, v]) => Number.isFinite(v)));
|
||||||
|
if (out.actions) {
|
||||||
|
const { actions, ...rest } = out;
|
||||||
|
return rest;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
})();
|
||||||
|
|
||||||
|
const persistPcGroupConfig = (updates) => {
|
||||||
|
if (typeof window === 'undefined') return;
|
||||||
|
try {
|
||||||
|
const raw = window.localStorage.getItem('customSettings');
|
||||||
|
const parsed = raw ? JSON.parse(raw) : {};
|
||||||
|
const group = parsed[groupKey] && typeof parsed[groupKey] === 'object' ? { ...parsed[groupKey] } : {};
|
||||||
|
if (updates.pcTableColumnOrder !== undefined) group.pcTableColumnOrder = updates.pcTableColumnOrder;
|
||||||
|
if (updates.pcTableColumnVisibility !== undefined) group.pcTableColumnVisibility = updates.pcTableColumnVisibility;
|
||||||
|
if (updates.pcTableColumns !== undefined) group.pcTableColumns = updates.pcTableColumns;
|
||||||
|
parsed[groupKey] = group;
|
||||||
|
window.localStorage.setItem('customSettings', JSON.stringify(parsed));
|
||||||
|
setConfigByGroup((prev) => ({ ...prev, [groupKey]: { ...prev[groupKey], ...updates } }));
|
||||||
|
onCustomSettingsChange?.();
|
||||||
|
} catch { }
|
||||||
|
};
|
||||||
|
|
||||||
|
const setColumnOrder = (nextOrderOrUpdater) => {
|
||||||
|
const next = typeof nextOrderOrUpdater === 'function'
|
||||||
|
? nextOrderOrUpdater(columnOrder)
|
||||||
|
: nextOrderOrUpdater;
|
||||||
|
persistPcGroupConfig({ pcTableColumnOrder: next });
|
||||||
|
};
|
||||||
|
const setColumnVisibility = (nextOrUpdater) => {
|
||||||
|
const next = typeof nextOrUpdater === 'function'
|
||||||
|
? nextOrUpdater(columnVisibility)
|
||||||
|
: nextOrUpdater;
|
||||||
|
persistPcGroupConfig({ pcTableColumnVisibility: next });
|
||||||
|
};
|
||||||
|
const setColumnSizing = (nextOrUpdater) => {
|
||||||
|
const next = typeof nextOrUpdater === 'function'
|
||||||
|
? nextOrUpdater(columnSizing)
|
||||||
|
: nextOrUpdater;
|
||||||
|
const { actions, ...rest } = next || {};
|
||||||
|
persistPcGroupConfig({ pcTableColumns: rest || {} });
|
||||||
|
};
|
||||||
const [settingModalOpen, setSettingModalOpen] = useState(false);
|
const [settingModalOpen, setSettingModalOpen] = useState(false);
|
||||||
const [resetConfirmOpen, setResetConfirmOpen] = useState(false);
|
const [resetConfirmOpen, setResetConfirmOpen] = useState(false);
|
||||||
const handleResetSizing = () => {
|
const handleResetSizing = () => {
|
||||||
setColumnSizing({});
|
setColumnSizing({});
|
||||||
persistColumnSizing({});
|
|
||||||
setResetConfirmOpen(false);
|
setResetConfirmOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleResetColumnOrder = () => {
|
const handleResetColumnOrder = () => {
|
||||||
const defaultOrder = [...NON_FROZEN_COLUMN_IDS];
|
setColumnOrder([...NON_FROZEN_COLUMN_IDS]);
|
||||||
setColumnOrder(defaultOrder);
|
|
||||||
persistColumnOrder(defaultOrder);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleResetColumnVisibility = () => {
|
const handleResetColumnVisibility = () => {
|
||||||
@@ -294,14 +323,9 @@ export default function PcFundTable({
|
|||||||
allVisible[id] = true;
|
allVisible[id] = true;
|
||||||
});
|
});
|
||||||
setColumnVisibility(allVisible);
|
setColumnVisibility(allVisible);
|
||||||
persistColumnVisibility(allVisible);
|
|
||||||
};
|
};
|
||||||
const handleToggleColumnVisibility = (columnId, visible) => {
|
const handleToggleColumnVisibility = (columnId, visible) => {
|
||||||
setColumnVisibility((prev = {}) => {
|
setColumnVisibility((prev = {}) => ({ ...prev, [columnId]: visible }));
|
||||||
const next = { ...prev, [columnId]: visible };
|
|
||||||
persistColumnVisibility(next);
|
|
||||||
return next;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
const onRemoveFundRef = useRef(onRemoveFund);
|
const onRemoveFundRef = useRef(onRemoveFund);
|
||||||
const onToggleFavoriteRef = useRef(onToggleFavorite);
|
const onToggleFavoriteRef = useRef(onToggleFavorite);
|
||||||
@@ -350,6 +374,7 @@ export default function PcFundTable({
|
|||||||
onRemoveFromGroupRef.current?.(original);
|
onRemoveFromGroupRef.current?.(original);
|
||||||
}}
|
}}
|
||||||
title="从小分组移除"
|
title="从小分组移除"
|
||||||
|
style={{ backgroundColor: 'transparent'}}
|
||||||
>
|
>
|
||||||
<ExitIcon width="18" height="18" style={{ transform: 'rotate(180deg)' }} />
|
<ExitIcon width="18" height="18" style={{ transform: 'rotate(180deg)' }} />
|
||||||
</button>
|
</button>
|
||||||
@@ -676,7 +701,6 @@ export default function PcFundTable({
|
|||||||
setColumnSizing((prev) => {
|
setColumnSizing((prev) => {
|
||||||
const next = typeof updater === 'function' ? updater(prev) : updater;
|
const next = typeof updater === 'function' ? updater(prev) : updater;
|
||||||
const { actions, ...rest } = next || {};
|
const { actions, ...rest } = next || {};
|
||||||
persistColumnSizing(rest || {});
|
|
||||||
return rest || {};
|
return rest || {};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -686,18 +710,10 @@ export default function PcFundTable({
|
|||||||
columnVisibility,
|
columnVisibility,
|
||||||
},
|
},
|
||||||
onColumnOrderChange: (updater) => {
|
onColumnOrderChange: (updater) => {
|
||||||
setColumnOrder((prev) => {
|
setColumnOrder(updater);
|
||||||
const next = typeof updater === 'function' ? updater(prev) : prev;
|
|
||||||
persistColumnOrder(next);
|
|
||||||
return next;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
onColumnVisibilityChange: (updater) => {
|
onColumnVisibilityChange: (updater) => {
|
||||||
setColumnVisibility((prev = {}) => {
|
setColumnVisibility(updater);
|
||||||
const next = typeof updater === 'function' ? updater(prev) : (updater || {});
|
|
||||||
persistColumnVisibility(next);
|
|
||||||
return next;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
initialState: {
|
initialState: {
|
||||||
columnPinning: {
|
columnPinning: {
|
||||||
@@ -927,7 +943,6 @@ export default function PcFundTable({
|
|||||||
columns={columnOrder.map((id) => ({ id, header: COLUMN_HEADERS[id] ?? id }))}
|
columns={columnOrder.map((id) => ({ id, header: COLUMN_HEADERS[id] ?? id }))}
|
||||||
onColumnReorder={(newOrder) => {
|
onColumnReorder={(newOrder) => {
|
||||||
setColumnOrder(newOrder);
|
setColumnOrder(newOrder);
|
||||||
persistColumnOrder(newOrder);
|
|
||||||
}}
|
}}
|
||||||
columnVisibility={columnVisibility}
|
columnVisibility={columnVisibility}
|
||||||
onToggleColumnVisibility={handleToggleColumnVisibility}
|
onToggleColumnVisibility={handleToggleColumnVisibility}
|
||||||
|
|||||||
@@ -1900,7 +1900,7 @@ input[type="number"] {
|
|||||||
|
|
||||||
.mobile-setting-drawer {
|
.mobile-setting-drawer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-height: 75vh;
|
max-height: 90vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
border-radius: 20px 20px 0 0;
|
border-radius: 20px 20px 0 0;
|
||||||
|
|||||||
33
app/page.jsx
33
app/page.jsx
@@ -1751,15 +1751,44 @@ export default function HomePage() {
|
|||||||
setGroups(next);
|
setGroups(next);
|
||||||
storageHelper.setItem('groups', JSON.stringify(next));
|
storageHelper.setItem('groups', JSON.stringify(next));
|
||||||
if (currentTab === id) setCurrentTab('all');
|
if (currentTab === id) setCurrentTab('all');
|
||||||
|
try {
|
||||||
|
const raw = window.localStorage.getItem('customSettings');
|
||||||
|
const parsed = raw ? JSON.parse(raw) : {};
|
||||||
|
if (parsed && typeof parsed === 'object' && parsed[id] !== undefined) {
|
||||||
|
delete parsed[id];
|
||||||
|
window.localStorage.setItem('customSettings', JSON.stringify(parsed));
|
||||||
|
triggerCustomSettingsSync();
|
||||||
|
}
|
||||||
|
} catch { }
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUpdateGroups = (newGroups) => {
|
const handleUpdateGroups = (newGroups) => {
|
||||||
|
const removedIds = groups.filter((g) => !newGroups.find((ng) => ng.id === g.id)).map((g) => g.id);
|
||||||
setGroups(newGroups);
|
setGroups(newGroups);
|
||||||
storageHelper.setItem('groups', JSON.stringify(newGroups));
|
storageHelper.setItem('groups', JSON.stringify(newGroups));
|
||||||
// 如果当前选中的分组被删除了,切换回“全部”
|
// 如果当前选中的分组被删除了,切换回“全部”
|
||||||
if (currentTab !== 'all' && currentTab !== 'fav' && !newGroups.find(g => g.id === currentTab)) {
|
if (currentTab !== 'all' && currentTab !== 'fav' && !newGroups.find(g => g.id === currentTab)) {
|
||||||
setCurrentTab('all');
|
setCurrentTab('all');
|
||||||
}
|
}
|
||||||
|
if (removedIds.length > 0) {
|
||||||
|
try {
|
||||||
|
const raw = window.localStorage.getItem('customSettings');
|
||||||
|
const parsed = raw ? JSON.parse(raw) : {};
|
||||||
|
if (parsed && typeof parsed === 'object') {
|
||||||
|
let changed = false;
|
||||||
|
removedIds.forEach((groupId) => {
|
||||||
|
if (parsed[groupId] !== undefined) {
|
||||||
|
delete parsed[groupId];
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (changed) {
|
||||||
|
window.localStorage.setItem('customSettings', JSON.stringify(parsed));
|
||||||
|
triggerCustomSettingsSync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddFundsToGroup = (codes) => {
|
const handleAddFundsToGroup = (codes) => {
|
||||||
@@ -3906,7 +3935,7 @@ export default function HomePage() {
|
|||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '48px',
|
height: '48px',
|
||||||
border: '2px dashed rgba(255,255,255,0.1)',
|
border: '2px dashed var(--border)',
|
||||||
background: 'transparent',
|
background: 'transparent',
|
||||||
borderRadius: '12px',
|
borderRadius: '12px',
|
||||||
color: 'var(--muted)',
|
color: 'var(--muted)',
|
||||||
@@ -3926,7 +3955,7 @@ export default function HomePage() {
|
|||||||
e.currentTarget.style.background = 'rgba(34, 211, 238, 0.05)';
|
e.currentTarget.style.background = 'rgba(34, 211, 238, 0.05)';
|
||||||
}}
|
}}
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
e.currentTarget.style.borderColor = 'rgba(255,255,255,0.1)';
|
e.currentTarget.style.borderColor = 'var(--border)';
|
||||||
e.currentTarget.style.color = 'var(--muted)';
|
e.currentTarget.style.color = 'var(--muted)';
|
||||||
e.currentTarget.style.background = 'transparent';
|
e.currentTarget.style.background = 'transparent';
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user