From f379c9fef5faa3f020e365dadf0e7b97c7f76c5c Mon Sep 17 00:00:00 2001
From: hzm <934585316@qq.com>
Date: Mon, 9 Mar 2026 08:41:01 +0800
Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E6=94=B9=E8=BF=9B=E7=A7=BB?=
=?UTF-8?q?=E5=8A=A8=E7=AB=AF=E8=A1=A8=E6=A0=BC=E8=A1=8C=E6=8E=92=E5=BA=8F?=
=?UTF-8?q?=E5=BD=A2=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/components/Announcement.jsx | 1 +
app/components/MobileFundTable.jsx | 80 ++++++++++++++++++++++++++----
app/globals.css | 5 ++
3 files changed, 77 insertions(+), 9 deletions(-)
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%;
}