diff --git a/app/components/Announcement.jsx b/app/components/Announcement.jsx index 8fb99e6..ba382f0 100644 --- a/app/components/Announcement.jsx +++ b/app/components/Announcement.jsx @@ -80,6 +80,7 @@ export default function Announcement() {

2. 引入 shadcn ui 组件库,逐步调整项目样式。

3. 列表模式表头固定。

4. 列表模式点击名称,展示基金详情弹框。

+

5. 移动端列表模式,在默认排序下支持表格行排序。

注:强烈建议苹果用户通过 Safari 浏览器→分享→添加应用到主屏幕,实现保存网页成APP效果。(安卓同理)

diff --git a/app/components/MobileFundTable.jsx b/app/components/MobileFundTable.jsx index 3d3740f..7a718a1 100644 --- a/app/components/MobileFundTable.jsx +++ b/app/components/MobileFundTable.jsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useMemo, useRef, useState } from 'react'; +import { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react'; import ReactDOM from 'react-dom'; import { AnimatePresence, motion } from 'framer-motion'; import { @@ -34,7 +34,7 @@ import { import FitText from './FitText'; import FundCard from './FundCard'; import MobileSettingModal from './MobileSettingModal'; -import { CloseIcon, ExitIcon, SettingsIcon, StarIcon } from './Icons'; +import { CloseIcon, DragIcon, ExitIcon, SettingsIcon, SortIcon, StarIcon } from './Icons'; const MOBILE_NON_FROZEN_COLUMN_IDS = [ 'yesterdayChangePercent', @@ -55,6 +55,8 @@ const MOBILE_COLUMN_HEADERS = { holdingProfit: '持有收益', }; +const RowSortableContext = createContext(null); + function SortableRow({ row, children, isTableDragging, disabled }) { const { attributes, @@ -84,7 +86,9 @@ function SortableRow({ row, children, isTableDragging, disabled }) { style={{ ...style, position: 'relative' }} {...attributes} > - {typeof children === 'function' ? children(setActivatorNodeRef, listeners) : children} + + {typeof children === 'function' ? children(setActivatorNodeRef, listeners) : children} + ); } @@ -123,9 +127,12 @@ export default function MobileFundTable({ blockDrawerClose = false, closeDrawerRef, }) { + const [isNameSortMode, setIsNameSortMode] = useState(false); + + // 排序模式下拖拽手柄无需长按,直接拖动即可;非排序模式长按整行触发拖拽 const sensors = useSensors( useSensor(PointerSensor, { - activationConstraint: { delay: 400, tolerance: 5 }, + activationConstraint: isNameSortMode ? { delay: 0, tolerance: 5 } : { delay: 400, tolerance: 5 }, }), useSensor(KeyboardSensor) ); @@ -297,6 +304,19 @@ export default function MobileFundTable({ }; const [settingModalOpen, setSettingModalOpen] = useState(false); + + useEffect(() => { + if (sortBy !== 'default') setIsNameSortMode(false); + }, [sortBy]); + + // 排序模式下,点击页面任意区域(含表格外)退出排序;使用冒泡阶段,避免先于排序按钮处理 + useEffect(() => { + if (!isNameSortMode) return; + const onDocClick = () => setIsNameSortMode(false); + document.addEventListener('click', onDocClick); + return () => document.removeEventListener('click', onDocClick); + }, [isNameSortMode]); + const [cardSheetRow, setCardSheetRow] = useState(null); const tableContainerRef = useRef(null); const portalHeaderRef = useRef(null); @@ -442,7 +462,8 @@ export default function MobileFundTable({ }; // 移动端名称列:无拖拽把手,长按整行触发排序;点击名称可打开底部卡片弹框(需传入 getFundCardProps) - const MobileFundNameCell = ({ info, showFullFundName, onOpenCardSheet }) => { + // 当 isNameSortMode 且 sortBy==='default' 时,左侧显示排序/拖拽图标,可拖动行排序 + const MobileFundNameCell = ({ info, showFullFundName, onOpenCardSheet, isNameSortMode: nameSortMode, sortBy: currentSortBy }) => { const original = info.row.original || {}; const code = original.code; const isUpdated = original.isUpdated; @@ -451,10 +472,23 @@ export default function MobileFundTable({ const holdingAmountDisplay = hasHoldingAmount ? (original.holdingAmount ?? '—') : null; const isFavorites = favorites?.has?.(code); const isGroupTab = currentTab && currentTab !== 'all' && currentTab !== 'fav'; + const rowSortable = useContext(RowSortableContext); + const showDragHandle = nameSortMode && currentSortBy === 'default' && rowSortable; return (
- {isGroupTab ? ( + {showDragHandle ? ( + e.stopPropagation()} + {...rowSortable.listeners} + > + + + ) : isGroupTab ? ( + {sortBy === 'default' && ( + + )}
), cell: (info) => ( @@ -588,6 +647,8 @@ export default function MobileFundTable({ info={info} showFullFundName={showFullFundName} onOpenCardSheet={getFundCardProps ? (row) => setCardSheetRow(row) : undefined} + isNameSortMode={isNameSortMode} + sortBy={sortBy} /> ), meta: { align: 'left', cellClassName: 'name-cell', width: columnWidthMap.fundName }, @@ -746,7 +807,7 @@ export default function MobileFundTable({ meta: { align: 'right', cellClassName: 'holding-cell', width: columnWidthMap.holdingProfit }, }, ], - [currentTab, favorites, refreshing, columnWidthMap, showFullFundName, getFundCardProps] + [currentTab, favorites, refreshing, columnWidthMap, showFullFundName, getFundCardProps, isNameSortMode, sortBy] ); const table = useReactTable({ @@ -934,7 +995,7 @@ export default function MobileFundTable({ > {(setActivatorNodeRef, listeners) => (
setIsNameSortMode(false) : undefined} + {...(sortBy === 'default' && !isNameSortMode ? listeners : {})} > {row.getVisibleCells().map((cell, cellIndex) => { const columnId = cell.column.id; diff --git a/app/globals.css b/app/globals.css index 5e053d7..ed828d2 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1695,6 +1695,11 @@ input[type="number"] { padding-left: 12px; } + /* 基金名称表头排序按钮在排序模式下的高亮 */ + .mobile-fund-table .mobile-fund-table-header .icon-button.active { + color: var(--primary); + } + .mobile-fund-table .table-row .name-cell .name-cell-content { min-height: 100%; }