From b489677d3e379779b2550628a89206ea2e124175 Mon Sep 17 00:00:00 2001 From: hzm <934585316@qq.com> Date: Tue, 17 Mar 2026 15:41:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=8A=A0=E4=BB=93=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E8=B4=B9=E7=8E=87=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/fund.js | 2 +- app/components/TradeModal.jsx | 224 ++++++++++++++++++++++++---------- components/ui/spinner.jsx | 21 ++++ package-lock.json | 2 +- 4 files changed, 182 insertions(+), 67 deletions(-) create mode 100644 components/ui/spinner.jsx diff --git a/app/api/fund.js b/app/api/fund.js index 7b815b6..8798556 100644 --- a/app/api/fund.js +++ b/app/api/fund.js @@ -720,7 +720,7 @@ const snapshotPingzhongdataGlobals = (fundCode) => { }; }; -const jsonpLoadPingzhongdata = (fundCode, timeoutMs = 10000) => { +const jsonpLoadPingzhongdata = (fundCode, timeoutMs = 20000) => { return new Promise((resolve, reject) => { if (typeof document === 'undefined' || !document.body) { reject(new Error('无浏览器环境')); diff --git a/app/components/TradeModal.jsx b/app/components/TradeModal.jsx index a5059df..024d26b 100644 --- a/app/components/TradeModal.jsx +++ b/app/components/TradeModal.jsx @@ -6,7 +6,7 @@ import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc'; import timezone from 'dayjs/plugin/timezone'; import { isNumber } from 'lodash'; -import { fetchSmartFundNetValue } from '../api/fund'; +import { fetchFundPingzhongdata, fetchSmartFundNetValue } from '../api/fund'; import { DatePicker, NumericInput } from './Common'; import ConfirmModal from './ConfirmModal'; import { CloseIcon } from './Icons'; @@ -16,6 +16,7 @@ import { DialogTitle, } from '@/components/ui/dialog'; import PendingTradesModal from './PendingTradesModal'; +import { Spinner } from '@/components/ui/spinner'; dayjs.extend(utc); dayjs.extend(timezone); @@ -39,12 +40,65 @@ export default function TradeModal({ type, fund, holding, onClose, onConfirm, pe const [share, setShare] = useState(''); const [amount, setAmount] = useState(''); const [feeRate, setFeeRate] = useState('0'); + const [minBuyAmount, setMinBuyAmount] = useState(0); + const [loadingBuyMeta, setLoadingBuyMeta] = useState(false); + const [buyMetaError, setBuyMetaError] = useState(null); const [date, setDate] = useState(() => { return formatDate(); }); const [isAfter3pm, setIsAfter3pm] = useState(nowInTz().hour() >= 15); const [calcShare, setCalcShare] = useState(null); + const parseNumberish = (input) => { + if (input === null || typeof input === 'undefined') return null; + if (typeof input === 'number') return Number.isFinite(input) ? input : null; + const cleaned = String(input).replace(/[^\d.]/g, ''); + const n = parseFloat(cleaned); + return Number.isFinite(n) ? n : null; + }; + + useEffect(() => { + if (!isBuy || !fund?.code) return; + let cancelled = false; + + setLoadingBuyMeta(true); + setBuyMetaError(null); + + fetchFundPingzhongdata(fund.code) + .then((pz) => { + if (cancelled) return; + const rate = parseNumberish(pz?.fund_Rate); + const minsg = parseNumberish(pz?.fund_minsg); + + if (Number.isFinite(minsg)) { + setMinBuyAmount(minsg); + } else { + setMinBuyAmount(0); + } + + if (Number.isFinite(rate)) { + setFeeRate((prev) => { + const prevNum = parseNumberish(prev); + const shouldOverride = prev === '' || prev === '0' || prevNum === 0 || prevNum === null; + return shouldOverride ? rate.toFixed(2) : prev; + }); + } + }) + .catch((e) => { + if (cancelled) return; + setBuyMetaError(e?.message || '买入信息加载失败'); + setMinBuyAmount(0); + }) + .finally(() => { + if (cancelled) return; + setLoadingBuyMeta(false); + }); + + return () => { + cancelled = true; + }; + }, [isBuy, fund?.code]); + const currentPendingTrades = useMemo(() => { return pendingTrades.filter(t => t.fundCode === fund?.code); }, [pendingTrades, fund]); @@ -148,7 +202,7 @@ export default function TradeModal({ type, fund, holding, onClose, onConfirm, pe }; const isValid = isBuy - ? (!!amount && !!feeRate && !!date && calcShare !== null) + ? (!!amount && !!feeRate && !!date && calcShare !== null && !loadingBuyMeta && (parseFloat(amount) || 0) >= (Number(minBuyAmount) || 0)) : (!!share && !!date); const handleSetShareFraction = (fraction) => { @@ -372,72 +426,112 @@ export default function TradeModal({ type, fund, holding, onClose, onConfirm, pe