From 1234653fa9ccdabe7fcb24a5a07b27f5f627204f Mon Sep 17 00:00:00 2001 From: hzm <934585316@qq.com> Date: Thu, 19 Feb 2026 10:54:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=BB=84=E4=BB=B6=E6=8B=86=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.json | 5 + app/components/AddFundToGroupModal.jsx | 90 + app/components/AddResultModal.jsx | 53 + app/components/CloudConfigModal.jsx | 54 + app/components/ConfirmModal.jsx | 43 + app/components/DonateModal.jsx | 37 + app/components/FeedbackModal.jsx | 148 ++ app/components/GroupManageModal.jsx | 195 ++ app/components/GroupModal.jsx | 61 + app/components/HoldingActionModal.jsx | 68 + app/components/HoldingEditModal.jsx | 241 +++ app/components/LoginModal.jsx | 97 + app/components/ScanImportConfirmModal.jsx | 86 + app/components/ScanImportProgressModal.jsx | 48 + app/components/ScanPickModal.jsx | 40 + app/components/ScanProgressModal.jsx | 54 + app/components/SettingsModal.jsx | 84 + app/components/SuccessModal.jsx | 35 + app/components/TradeModal.jsx | 697 +++++++ app/components/UpdatePromptModal.jsx | 72 + app/components/WeChatModal.jsx | 45 + app/page.jsx | 2167 +------------------- 22 files changed, 2320 insertions(+), 2100 deletions(-) create mode 100644 .eslintrc.json create mode 100644 app/components/AddFundToGroupModal.jsx create mode 100644 app/components/AddResultModal.jsx create mode 100644 app/components/CloudConfigModal.jsx create mode 100644 app/components/ConfirmModal.jsx create mode 100644 app/components/DonateModal.jsx create mode 100644 app/components/FeedbackModal.jsx create mode 100644 app/components/GroupManageModal.jsx create mode 100644 app/components/GroupModal.jsx create mode 100644 app/components/HoldingActionModal.jsx create mode 100644 app/components/HoldingEditModal.jsx create mode 100644 app/components/LoginModal.jsx create mode 100644 app/components/ScanImportConfirmModal.jsx create mode 100644 app/components/ScanImportProgressModal.jsx create mode 100644 app/components/ScanPickModal.jsx create mode 100644 app/components/ScanProgressModal.jsx create mode 100644 app/components/SettingsModal.jsx create mode 100644 app/components/SuccessModal.jsx create mode 100644 app/components/TradeModal.jsx create mode 100644 app/components/UpdatePromptModal.jsx create mode 100644 app/components/WeChatModal.jsx diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..5cac8dc --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "next/core-web-vitals" + ] +} \ No newline at end of file diff --git a/app/components/AddFundToGroupModal.jsx b/app/components/AddFundToGroupModal.jsx new file mode 100644 index 0000000..87dfb8f --- /dev/null +++ b/app/components/AddFundToGroupModal.jsx @@ -0,0 +1,90 @@ +'use client'; + +import { useState } from 'react'; +import { motion } from 'framer-motion'; +import { CloseIcon, PlusIcon } from './Icons'; + +export default function AddFundToGroupModal({ allFunds, currentGroupCodes, onClose, onAdd }) { + const [selected, setSelected] = useState(new Set()); + + const availableFunds = (allFunds || []).filter(f => !(currentGroupCodes || []).includes(f.code)); + + const toggleSelect = (code) => { + setSelected(prev => { + const next = new Set(prev); + if (next.has(code)) next.delete(code); + else next.add(code); + return next; + }); + }; + + return ( + + e.stopPropagation()} + > +
+
+ + 添加基金到分组 +
+ +
+ +
+ {availableFunds.length === 0 ? ( +
+

所有基金已在该分组中

+
+ ) : ( +
+ {availableFunds.map((fund) => ( +
toggleSelect(fund.code)} + style={{ cursor: 'pointer' }} + > +
+ {selected.has(fund.code) &&
} +
+
+
{fund.name}
+
#{fund.code}
+
+
+ ))} +
+ )} +
+ +
+ + +
+ + + ); +} diff --git a/app/components/AddResultModal.jsx b/app/components/AddResultModal.jsx new file mode 100644 index 0000000..491baa2 --- /dev/null +++ b/app/components/AddResultModal.jsx @@ -0,0 +1,53 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { CloseIcon, SettingsIcon } from './Icons'; + +export default function AddResultModal({ failures, onClose }) { + return ( + + e.stopPropagation()} + > +
+
+ + 部分基金添加失败 +
+ +
+
+ 未获取到估值数据的基金如下: +
+
+ {failures.map((it, idx) => ( +
+ {it.name || '未知名称'} +
+ #{it.code} +
+
+ ))} +
+
+ +
+
+
+ ); +} diff --git a/app/components/CloudConfigModal.jsx b/app/components/CloudConfigModal.jsx new file mode 100644 index 0000000..d62781a --- /dev/null +++ b/app/components/CloudConfigModal.jsx @@ -0,0 +1,54 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { CloseIcon, CloudIcon } from './Icons'; + +export default function CloudConfigModal({ onConfirm, onCancel, type = 'empty' }) { + const isConflict = type === 'conflict'; + return ( + + e.stopPropagation()} + > +
+
+ + {isConflict ? '发现配置冲突' : '云端暂无配置'} +
+ {!isConflict && ( + + )} +
+

+ {isConflict + ? '检测到本地配置与云端不一致,请选择操作:' + : '是否将本地配置同步到云端?'} +

+
+ + +
+
+
+ ); +} diff --git a/app/components/ConfirmModal.jsx b/app/components/ConfirmModal.jsx new file mode 100644 index 0000000..ea32584 --- /dev/null +++ b/app/components/ConfirmModal.jsx @@ -0,0 +1,43 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { TrashIcon } from './Icons'; + +export default function ConfirmModal({ title, message, onConfirm, onCancel, confirmText = "确定删除" }) { + return ( + { + e.stopPropagation(); + onCancel(); + }} + initial={{ opacity: 0 }} + animate={{ opacity: 1 }} + exit={{ opacity: 0 }} + style={{ zIndex: 10002 }} + > + e.stopPropagation()} + > +
+ + {title} +
+

+ {message} +

+
+ + +
+
+
+ ); +} diff --git a/app/components/DonateModal.jsx b/app/components/DonateModal.jsx new file mode 100644 index 0000000..1a41e40 --- /dev/null +++ b/app/components/DonateModal.jsx @@ -0,0 +1,37 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { CloseIcon } from './Icons'; +import { DonateTabs } from './Common'; + +export default function DonateModal({ onClose }) { + return ( +
+ e.stopPropagation()} + > +
+
+ ☕ 请作者喝杯咖啡 +
+ +
+ +
+ +
+ +
+ 感谢您的支持!您的鼓励是我持续维护和更新的动力。 +
+
+
+ ); +} diff --git a/app/components/FeedbackModal.jsx b/app/components/FeedbackModal.jsx new file mode 100644 index 0000000..2290d20 --- /dev/null +++ b/app/components/FeedbackModal.jsx @@ -0,0 +1,148 @@ +'use client'; + +import { useState } from 'react'; +import { motion } from 'framer-motion'; +import { CloseIcon, SettingsIcon } from './Icons'; +import { submitFeedback } from '../api/fund'; + +export default function FeedbackModal({ onClose, user, onOpenWeChat }) { + const [submitting, setSubmitting] = useState(false); + const [succeeded, setSucceeded] = useState(false); + const [error, setError] = useState(""); + + const onSubmit = async (e) => { + e.preventDefault(); + setSubmitting(true); + setError(""); + + const formData = new FormData(e.target); + const nickname = formData.get("nickname")?.trim(); + if (!nickname) { + formData.set("nickname", "匿名"); + } + + // Web3Forms Access Key + formData.append("access_key", process.env.NEXT_PUBLIC_WEB3FORMS_ACCESS_KEY || ''); + formData.append("subject", "基估宝 - 用户反馈"); + + try { + const data = await submitFeedback(formData); + if (data.success) { + setSucceeded(true); + } else { + setError(data.message || "提交失败,请稍后再试"); + } + } catch (err) { + setError("网络错误,请检查您的连接"); + } finally { + setSubmitting(false); + } + }; + + return ( + + e.stopPropagation()} + > +
+
+ + 意见反馈 +
+ +
+ + {succeeded ? ( +
+
🎉
+

感谢您的反馈!

+

我们已收到您的建议,会尽快查看。

+ +
+ ) : ( +
+
+ + +
+ +
+ +