feat: PC端列表模式点击基金名称查看详情
This commit is contained in:
@@ -220,25 +220,25 @@ export default function FundCard({
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: 4,
|
gap: 4,
|
||||||
cursor: 'pointer',
|
cursor: layoutMode === 'drawer' ? 'default' : 'pointer',
|
||||||
}}
|
}}
|
||||||
onClick={() => onHoldingClick?.(f)}
|
onClick={() => layoutMode !== 'drawer' && onHoldingClick?.(f)}
|
||||||
>
|
>
|
||||||
未设置 <SettingsIcon width="12" height="12" />
|
未设置 {layoutMode !== 'drawer' && <SettingsIcon width="12" height="12" />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
className="stat"
|
className="stat"
|
||||||
style={{ cursor: 'pointer', flexDirection: 'column', gap: 4 }}
|
style={{ cursor: layoutMode === 'drawer' ? 'default' : 'pointer', flexDirection: 'column', gap: 4 }}
|
||||||
onClick={() => onActionClick?.(f)}
|
onClick={() => layoutMode !== 'drawer' && onActionClick?.(f)}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="label"
|
className="label"
|
||||||
style={{ display: 'flex', alignItems: 'center', gap: 4 }}
|
style={{ display: 'flex', alignItems: 'center', gap: 4 }}
|
||||||
>
|
>
|
||||||
持仓金额 <SettingsIcon width="12" height="12" style={{ opacity: 0.7 }} />
|
持仓金额 {layoutMode !== 'drawer' && <SettingsIcon width="12" height="12" style={{ opacity: 0.7 }} />}
|
||||||
</span>
|
</span>
|
||||||
<span className="value">¥{profit.amount.toFixed(2)}</span>
|
<span className="value">¥{profit.amount.toFixed(2)}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -267,12 +267,12 @@ export default function FundCard({
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onPercentModeToggle?.(f.code);
|
onPercentModeToggle?.(f.code);
|
||||||
}}
|
}}
|
||||||
style={{ cursor: 'pointer', flexDirection: 'column', gap: 4 }}
|
style={{ cursor: 'pointer', flexDirection: 'column', gap: 4, alignItems: 'flex-end' }}
|
||||||
title="点击切换金额/百分比"
|
title="点击切换金额/百分比"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="label"
|
className="label"
|
||||||
style={{ display: 'flex', alignItems: 'center', gap: 1 }}
|
style={{ display: 'flex', alignItems: 'center', gap: 1, justifyContent: 'flex-end' }}
|
||||||
>
|
>
|
||||||
持有收益{percentModes?.[f.code] ? '(%)' : ''}
|
持有收益{percentModes?.[f.code] ? '(%)' : ''}
|
||||||
<SwitchIcon />
|
<SwitchIcon />
|
||||||
|
|||||||
@@ -267,13 +267,13 @@ export function SwitchIcon({ props }) {
|
|||||||
p-id="2524" width="13" height="13">
|
p-id="2524" width="13" height="13">
|
||||||
<path
|
<path
|
||||||
d="M885.247 477.597H132c-17.673 0-32-14.327-32-32s14.327-32 32-32h753.247c17.673 0 32 14.327 32 32s-14.327 32-32 32z"
|
d="M885.247 477.597H132c-17.673 0-32-14.327-32-32s14.327-32 32-32h753.247c17.673 0 32 14.327 32 32s-14.327 32-32 32z"
|
||||||
fill="" p-id="2525"></path>
|
fill="currentColor" p-id="2525"></path>
|
||||||
<path
|
<path
|
||||||
d="M893.366 477.392c-8.189 0-16.379-3.124-22.627-9.373L709.954 307.235c-12.497-12.497-12.497-32.758 0-45.255 12.496-12.497 32.758-12.497 45.254 0l160.785 160.785c12.497 12.497 12.497 32.758 0 45.255-6.248 6.248-14.437 9.372-22.627 9.372zM893.366 609.607H140.119c-17.673 0-32-14.327-32-32s14.327-32 32-32h753.248c17.673 0 32 14.327 32 32s-14.328 32-32.001 32z"
|
d="M893.366 477.392c-8.189 0-16.379-3.124-22.627-9.373L709.954 307.235c-12.497-12.497-12.497-32.758 0-45.255 12.496-12.497 32.758-12.497 45.254 0l160.785 160.785c12.497 12.497 12.497 32.758 0 45.255-6.248 6.248-14.437 9.372-22.627 9.372zM893.366 609.607H140.119c-17.673 0-32-14.327-32-32s14.327-32 32-32h753.248c17.673 0 32 14.327 32 32s-14.328 32-32.001 32z"
|
||||||
fill="" p-id="2526"></path>
|
fill="currentColor" p-id="2526"></path>
|
||||||
<path
|
<path
|
||||||
d="M292.784 770.597c-8.189 0-16.379-3.124-22.627-9.373L109.373 600.439c-12.497-12.496-12.497-32.758 0-45.254 12.497-12.498 32.758-12.498 45.255 0L315.412 715.97c12.497 12.496 12.497 32.758 0 45.254-6.249 6.249-14.438 9.373-22.628 9.373z"
|
d="M292.784 770.597c-8.189 0-16.379-3.124-22.627-9.373L109.373 600.439c-12.497-12.496-12.497-32.758 0-45.254 12.497-12.498 32.758-12.498 45.255 0L315.412 715.97c12.497 12.496 12.497 32.758 0 45.254-6.249 6.249-14.438 9.373-22.628 9.373z"
|
||||||
fill="" p-id="2527"></path>
|
fill="currentColor" p-id="2527"></path>
|
||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,14 @@ import { CSS } from '@dnd-kit/utilities';
|
|||||||
import ConfirmModal from './ConfirmModal';
|
import ConfirmModal from './ConfirmModal';
|
||||||
import FitText from './FitText';
|
import FitText from './FitText';
|
||||||
import PcTableSettingModal from './PcTableSettingModal';
|
import PcTableSettingModal from './PcTableSettingModal';
|
||||||
import { DragIcon, ExitIcon, SettingsIcon, StarIcon, TrashIcon } from './Icons';
|
import FundCard from './FundCard';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@/components/ui/dialog';
|
||||||
|
import { CloseIcon, DragIcon, ExitIcon, SettingsIcon, StarIcon, TrashIcon } from './Icons';
|
||||||
|
|
||||||
const NON_FROZEN_COLUMN_IDS = [
|
const NON_FROZEN_COLUMN_IDS = [
|
||||||
'yesterdayChangePercent',
|
'yesterdayChangePercent',
|
||||||
@@ -118,6 +125,9 @@ function SortableRow({ row, children, isTableDragging, disabled }) {
|
|||||||
* @param {(row: any) => void} [props.onRemoveFromGroup] - 从当前分组移除
|
* @param {(row: any) => void} [props.onRemoveFromGroup] - 从当前分组移除
|
||||||
* @param {(row: any, meta: { hasHolding: boolean }) => void} [props.onHoldingAmountClick] - 点击持仓金额
|
* @param {(row: any, meta: { hasHolding: boolean }) => void} [props.onHoldingAmountClick] - 点击持仓金额
|
||||||
* @param {boolean} [props.refreshing] - 是否处于刷新状态(控制删除按钮禁用态)
|
* @param {boolean} [props.refreshing] - 是否处于刷新状态(控制删除按钮禁用态)
|
||||||
|
* @param {(row: any) => Object} [props.getFundCardProps] - 给定行返回 FundCard 的 props;传入后点击基金名称将用弹框展示卡片详情
|
||||||
|
* @param {React.MutableRefObject<(() => void) | null>} [props.closeDialogRef] - 注入关闭弹框的方法,用于确认删除时关闭
|
||||||
|
* @param {boolean} [props.blockDialogClose] - 为 true 时阻止点击遮罩关闭弹框(如删除确认弹框打开时)
|
||||||
*/
|
*/
|
||||||
export default function PcFundTable({
|
export default function PcFundTable({
|
||||||
data = [],
|
data = [],
|
||||||
@@ -132,6 +142,9 @@ export default function PcFundTable({
|
|||||||
sortBy = 'default',
|
sortBy = 'default',
|
||||||
onReorder,
|
onReorder,
|
||||||
onCustomSettingsChange,
|
onCustomSettingsChange,
|
||||||
|
getFundCardProps,
|
||||||
|
closeDialogRef,
|
||||||
|
blockDialogClose = false,
|
||||||
}) {
|
}) {
|
||||||
const sensors = useSensors(
|
const sensors = useSensors(
|
||||||
useSensor(PointerSensor, {
|
useSensor(PointerSensor, {
|
||||||
@@ -143,6 +156,7 @@ export default function PcFundTable({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const [activeId, setActiveId] = useState(null);
|
const [activeId, setActiveId] = useState(null);
|
||||||
|
const [cardDialogRow, setCardDialogRow] = useState(null);
|
||||||
|
|
||||||
const handleDragStart = (event) => {
|
const handleDragStart = (event) => {
|
||||||
setActiveId(event.active.id);
|
setActiveId(event.active.id);
|
||||||
@@ -341,6 +355,13 @@ export default function PcFundTable({
|
|||||||
const onRemoveFromGroupRef = useRef(onRemoveFromGroup);
|
const onRemoveFromGroupRef = useRef(onRemoveFromGroup);
|
||||||
const onHoldingAmountClickRef = useRef(onHoldingAmountClick);
|
const onHoldingAmountClickRef = useRef(onHoldingAmountClick);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (closeDialogRef) {
|
||||||
|
closeDialogRef.current = () => setCardDialogRow(null);
|
||||||
|
return () => { closeDialogRef.current = null; };
|
||||||
|
}
|
||||||
|
}, [closeDialogRef]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onRemoveFundRef.current = onRemoveFund;
|
onRemoveFundRef.current = onRemoveFund;
|
||||||
onToggleFavoriteRef.current = onToggleFavorite;
|
onToggleFavoriteRef.current = onToggleFavorite;
|
||||||
@@ -353,7 +374,7 @@ export default function PcFundTable({
|
|||||||
onHoldingAmountClick,
|
onHoldingAmountClick,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const FundNameCell = ({ info, showFullFundName }) => {
|
const FundNameCell = ({ info, showFullFundName, onOpenCardDialog }) => {
|
||||||
const original = info.row.original || {};
|
const original = info.row.original || {};
|
||||||
const code = original.code;
|
const code = original.code;
|
||||||
const isUpdated = original.isUpdated;
|
const isUpdated = original.isUpdated;
|
||||||
@@ -400,7 +421,15 @@ export default function PcFundTable({
|
|||||||
<StarIcon width="18" height="18" filled={isFavorites} />
|
<StarIcon width="18" height="18" filled={isFavorites} />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<div className="title-text">
|
<div
|
||||||
|
className="title-text"
|
||||||
|
role={onOpenCardDialog ? 'button' : undefined}
|
||||||
|
tabIndex={onOpenCardDialog ? 0 : undefined}
|
||||||
|
onClick={onOpenCardDialog ? (e) => { e.stopPropagation?.(); onOpenCardDialog(original); } : undefined}
|
||||||
|
onKeyDown={onOpenCardDialog ? (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onOpenCardDialog(original); } } : undefined}
|
||||||
|
style={onOpenCardDialog ? { cursor: 'pointer' } : undefined}
|
||||||
|
title={onOpenCardDialog ? '查看基金详情' : (original.isUpdated ? '今日净值已更新' : undefined)}
|
||||||
|
>
|
||||||
<span
|
<span
|
||||||
className={`name-text ${showFullFundName ? 'show-full' : ''}`}
|
className={`name-text ${showFullFundName ? 'show-full' : ''}`}
|
||||||
title={isUpdated ? '今日净值已更新' : ''}
|
title={isUpdated ? '今日净值已更新' : ''}
|
||||||
@@ -425,7 +454,13 @@ export default function PcFundTable({
|
|||||||
size: 265,
|
size: 265,
|
||||||
minSize: 140,
|
minSize: 140,
|
||||||
enablePinning: true,
|
enablePinning: true,
|
||||||
cell: (info) => <FundNameCell info={info} showFullFundName={showFullFundName} />,
|
cell: (info) => (
|
||||||
|
<FundNameCell
|
||||||
|
info={info}
|
||||||
|
showFullFundName={showFullFundName}
|
||||||
|
onOpenCardDialog={getFundCardProps ? (row) => setCardDialogRow(row) : undefined}
|
||||||
|
/>
|
||||||
|
),
|
||||||
meta: {
|
meta: {
|
||||||
align: 'left',
|
align: 'left',
|
||||||
cellClassName: 'name-cell',
|
cellClassName: 'name-cell',
|
||||||
@@ -732,7 +767,7 @@ export default function PcFundTable({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[currentTab, favorites, refreshing, sortBy, showFullFundName],
|
[currentTab, favorites, refreshing, sortBy, showFullFundName, getFundCardProps],
|
||||||
);
|
);
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
@@ -997,6 +1032,40 @@ export default function PcFundTable({
|
|||||||
showFullFundName={showFullFundName}
|
showFullFundName={showFullFundName}
|
||||||
onToggleShowFullFundName={handleToggleShowFullFundName}
|
onToggleShowFullFundName={handleToggleShowFullFundName}
|
||||||
/>
|
/>
|
||||||
|
<Dialog
|
||||||
|
open={!!(cardDialogRow && getFundCardProps)}
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
if (!open && !blockDialogClose) setCardDialogRow(null);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogContent
|
||||||
|
className="sm:max-w-2xl max-h-[88vh] flex flex-col p-0 overflow-hidden"
|
||||||
|
showCloseButton={false}
|
||||||
|
onPointerDownOutside={blockDialogClose ? (e) => e.preventDefault() : undefined}
|
||||||
|
>
|
||||||
|
<DialogHeader className="flex-shrink-0 flex flex-row items-center justify-between gap-2 space-y-0 px-6 pb-4 pt-6 text-left border-b border-[var(--border)]">
|
||||||
|
<DialogTitle className="text-base font-semibold text-[var(--text)]">
|
||||||
|
基金详情
|
||||||
|
</DialogTitle>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="icon-button rounded-lg"
|
||||||
|
aria-label="关闭"
|
||||||
|
onClick={() => setCardDialogRow(null)}
|
||||||
|
style={{ padding: 4, borderColor: 'transparent' }}
|
||||||
|
>
|
||||||
|
<CloseIcon width="20" height="20" />
|
||||||
|
</button>
|
||||||
|
</DialogHeader>
|
||||||
|
<div
|
||||||
|
className="flex-1 min-h-0 overflow-y-auto px-6 py-4"
|
||||||
|
>
|
||||||
|
{cardDialogRow && getFundCardProps ? (
|
||||||
|
<FundCard {...getFundCardProps(cardDialogRow)} layoutMode="drawer" />
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
37
app/page.jsx
37
app/page.jsx
@@ -319,6 +319,7 @@ export default function HomePage() {
|
|||||||
const tabsRef = useRef(null);
|
const tabsRef = useRef(null);
|
||||||
const [fundDeleteConfirm, setFundDeleteConfirm] = useState(null); // { code, name }
|
const [fundDeleteConfirm, setFundDeleteConfirm] = useState(null); // { code, name }
|
||||||
const fundDetailDrawerCloseRef = useRef(null); // 由 MobileFundTable 注入,用于确认删除时关闭基金详情 Drawer
|
const fundDetailDrawerCloseRef = useRef(null); // 由 MobileFundTable 注入,用于确认删除时关闭基金详情 Drawer
|
||||||
|
const fundDetailDialogCloseRef = useRef(null); // 由 PcFundTable 注入,用于确认删除时关闭基金详情 Dialog
|
||||||
|
|
||||||
const todayStr = formatDate();
|
const todayStr = formatDate();
|
||||||
|
|
||||||
@@ -2445,6 +2446,8 @@ export default function HomePage() {
|
|||||||
if (hasHolding) {
|
if (hasHolding) {
|
||||||
setFundDeleteConfirm({ code: fund.code, name: fund.name });
|
setFundDeleteConfirm({ code: fund.code, name: fund.name });
|
||||||
} else {
|
} else {
|
||||||
|
fundDetailDrawerCloseRef.current?.();
|
||||||
|
fundDetailDialogCloseRef.current?.();
|
||||||
removeFund(fund.code);
|
removeFund(fund.code);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -4021,6 +4024,39 @@ export default function HomePage() {
|
|||||||
setPercentModes(prev => ({ ...prev, [row.code]: !prev[row.code] }));
|
setPercentModes(prev => ({ ...prev, [row.code]: !prev[row.code] }));
|
||||||
}}
|
}}
|
||||||
onCustomSettingsChange={triggerCustomSettingsSync}
|
onCustomSettingsChange={triggerCustomSettingsSync}
|
||||||
|
closeDialogRef={fundDetailDialogCloseRef}
|
||||||
|
blockDialogClose={!!fundDeleteConfirm}
|
||||||
|
getFundCardProps={(row) => {
|
||||||
|
const fund = row?.rawFund || (row ? { code: row.code, name: row.fundName } : null);
|
||||||
|
if (!fund) return {};
|
||||||
|
return {
|
||||||
|
fund,
|
||||||
|
todayStr,
|
||||||
|
currentTab,
|
||||||
|
favorites,
|
||||||
|
dcaPlans,
|
||||||
|
holdings,
|
||||||
|
percentModes,
|
||||||
|
valuationSeries,
|
||||||
|
collapsedCodes,
|
||||||
|
collapsedTrends,
|
||||||
|
transactions,
|
||||||
|
theme,
|
||||||
|
isTradingDay,
|
||||||
|
refreshing,
|
||||||
|
getHoldingProfit,
|
||||||
|
onRemoveFromGroup: removeFundFromCurrentGroup,
|
||||||
|
onToggleFavorite: toggleFavorite,
|
||||||
|
onRemoveFund: requestRemoveFund,
|
||||||
|
onHoldingClick: (f) => setHoldingModal({ open: true, fund: f }),
|
||||||
|
onActionClick: (f) => setActionModal({ open: true, fund: f }),
|
||||||
|
onPercentModeToggle: (code) =>
|
||||||
|
setPercentModes((prev) => ({ ...prev, [code]: !prev[code] })),
|
||||||
|
onToggleCollapse: toggleCollapse,
|
||||||
|
onToggleTrendCollapse: toggleTrendCollapse,
|
||||||
|
layoutMode: 'drawer',
|
||||||
|
};
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -4156,6 +4192,7 @@ export default function HomePage() {
|
|||||||
confirmText="确定删除"
|
confirmText="确定删除"
|
||||||
onConfirm={() => {
|
onConfirm={() => {
|
||||||
fundDetailDrawerCloseRef.current?.();
|
fundDetailDrawerCloseRef.current?.();
|
||||||
|
fundDetailDialogCloseRef.current?.();
|
||||||
removeFund(fundDeleteConfirm.code);
|
removeFund(fundDeleteConfirm.code);
|
||||||
setFundDeleteConfirm(null);
|
setFundDeleteConfirm(null);
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user