"use client"; import { useEffect, useState } from "react"; import { AnimatePresence, motion, Reorder } from "framer-motion"; import { createPortal } from "react-dom"; import { Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerClose, } from "@/components/ui/drawer"; import { CloseIcon, DragIcon, ResetIcon, SettingsIcon } from "./Icons"; import ConfirmModal from "./ConfirmModal"; /** * 排序个性化设置弹框 * * - 移动端:使用 Drawer(自底向上抽屉,参考市场指数设置) * - PC 端:使用右侧侧弹框(样式参考 PcTableSettingModal) * * @param {Object} props * @param {boolean} props.open - 是否打开 * @param {() => void} props.onClose - 关闭回调 * @param {boolean} props.isMobile - 是否为移动端(由上层传入) * @param {Array<{id: string, label: string, enabled: boolean}>} props.rules - 排序规则列表 * @param {(nextRules: Array<{id: string, label: string, enabled: boolean}>) => void} props.onChangeRules - 规则变更回调 */ export default function SortSettingModal({ open, onClose, isMobile, rules = [], onChangeRules, onResetRules, }) { const [localRules, setLocalRules] = useState(rules); const [editingId, setEditingId] = useState(null); const [editingAlias, setEditingAlias] = useState(""); const [resetConfirmOpen, setResetConfirmOpen] = useState(false); useEffect(() => { if (open) { const defaultRule = (rules || []).find((item) => item.id === "default"); const otherRules = (rules || []).filter((item) => item.id !== "default"); const ordered = defaultRule ? [defaultRule, ...otherRules] : otherRules; setLocalRules(ordered); const prev = document.body.style.overflow; document.body.style.overflow = "hidden"; return () => { document.body.style.overflow = prev; }; } }, [open, rules]); const handleReorder = (nextItems) => { // 基于当前 localRules 计算新顺序(默认规则固定在首位) const defaultRule = (localRules || []).find((item) => item.id === "default"); const combined = defaultRule ? [defaultRule, ...nextItems] : nextItems; setLocalRules(combined); if (onChangeRules) { queueMicrotask(() => { onChangeRules(combined); }); } }; const handleToggle = (id) => { const next = (localRules || []).map((item) => item.id === id ? { ...item, enabled: !item.enabled } : item ); setLocalRules(next); if (onChangeRules) { queueMicrotask(() => { onChangeRules(next); }); } }; const startEditAlias = (item) => { if (!item || item.id === "default") return; setEditingId(item.id); setEditingAlias(item.alias || ""); }; const commitAlias = () => { if (!editingId) return; let nextRules = null; setLocalRules((prev) => { const next = prev.map((item) => item.id === editingId ? { ...item, alias: editingAlias.trim() || undefined } : item ); nextRules = next; return next; }); if (nextRules) { // 将父组件状态更新放到微任务中,避免在 SortSettingModal 渲染过程中直接更新 HomePage queueMicrotask(() => { onChangeRules?.(nextRules); }); } setEditingId(null); setEditingAlias(""); }; const cancelAlias = () => { setEditingId(null); setEditingAlias(""); }; if (!open) return null; const body = (

排序规则

{onResetRules && ( )}

可拖拽调整优先级,右侧开关控制是否启用该排序规则。点击规则名称可编辑别名(例如“估值涨幅”的别名为“涨跌幅”)。

{localRules.length === 0 ? (
暂无可配置的排序规则。
) : ( <> {/* 默认排序固定在顶部,且不可排序、不可关闭 */} {localRules.find((item) => item.id === "default") && (
{localRules.find((item) => item.id === "default")?.label || "默认"}
)} {/* 其他规则支持拖拽和开关 */} item.id !== "default")} onReorder={handleReorder} className={isMobile ? "mobile-setting-list" : "pc-table-setting-list"} layoutScroll={isMobile} style={isMobile ? { touchAction: "none" } : undefined} > {localRules .filter((item) => item.id !== "default") .map((item) => (
{editingId === item.id ? (
setEditingAlias(e.target.value)} onBlur={commitAlias} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); commitAlias(); } else if (e.key === "Escape") { e.preventDefault(); cancelAlias(); } }} placeholder="输入别名,如涨跌幅" style={{ flex: 1, // 使用 >=16px 的字号,避免移动端聚焦时页面放大 fontSize: 16, padding: "4px 8px", borderRadius: 6, border: "1px solid var(--border)", background: "transparent", color: "var(--text)", outline: "none", }} />
) : ( <> {item.alias && ( {item.alias} )} )}
{item.id !== "default" && ( )}
))}
)}
); const resetConfirm = ( {resetConfirmOpen && ( } confirmVariant="primary" confirmText="恢复默认" onConfirm={() => { setResetConfirmOpen(false); queueMicrotask(() => { onResetRules?.(); }); }} onCancel={() => setResetConfirmOpen(false)} /> )} ); if (isMobile) { return ( { if (!v) onClose?.(); }} direction="bottom" > 排序个性化设置
{body}
{resetConfirm}
); } if (typeof document === "undefined") return null; const content = ( {open && ( e.stopPropagation()} style={{ width: 420, maxWidth: 480, }} >
排序个性化设置
{body}
)}
); return createPortal( <> {content} {resetConfirm} , document.body ); }