'use client'; import { useEffect, useRef, useState, useMemo, useLayoutEffect } from 'react'; import { motion, AnimatePresence, Reorder } from 'framer-motion'; import Announcement from "./components/Announcement"; import zhifubaoImg from "./assets/zhifubao.jpg"; import weixinImg from "./assets/weixin.jpg"; import githubImg from "./assets/github.svg"; function PlusIcon(props) { return ( ); } function TrashIcon(props) { return ( ); } function SettingsIcon(props) { return ( ); } function RefreshIcon(props) { return ( ); } function ChevronIcon(props) { return ( ); } function SortIcon(props) { return ( ); } function GridIcon(props) { return ( ); } function CloseIcon(props) { return ( ); } function ExitIcon(props) { return ( ); } function ListIcon(props) { return ( ); } function DragIcon(props) { return ( ); } function FolderPlusIcon(props) { return ( ); } function StarIcon({ filled, ...props }) { return ( ); } function CalendarIcon(props) { return ( ); } function DatePicker({ value, onChange }) { const [isOpen, setIsOpen] = useState(false); const [currentMonth, setCurrentMonth] = useState(() => value ? new Date(value) : new Date()); // 点击外部关闭 useEffect(() => { const close = () => setIsOpen(false); if (isOpen) window.addEventListener('click', close); return () => window.removeEventListener('click', close); }, [isOpen]); const year = currentMonth.getFullYear(); const month = currentMonth.getMonth(); // 0-11 const handlePrevMonth = (e) => { e.stopPropagation(); setCurrentMonth(new Date(year, month - 1, 1)); }; const handleNextMonth = (e) => { e.stopPropagation(); setCurrentMonth(new Date(year, month + 1, 1)); }; const handleSelect = (e, day) => { e.stopPropagation(); const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`; // 检查是否是未来日期 const today = new Date(); today.setHours(0, 0, 0, 0); const selectedDate = new Date(dateStr); if (selectedDate > today) return; // 禁止选择未来日期 onChange(dateStr); setIsOpen(false); }; // 生成日历数据 const daysInMonth = new Date(year, month + 1, 0).getDate(); const firstDayOfWeek = new Date(year, month, 1).getDay(); // 0(Sun)-6(Sat) const days = []; for (let i = 0; i < firstDayOfWeek; i++) days.push(null); for (let i = 1; i <= daysInMonth; i++) days.push(i); return (
e.stopPropagation()}>
setIsOpen(!isOpen)} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0 12px', height: '40px', background: 'rgba(0,0,0,0.2)', borderRadius: '8px', cursor: 'pointer', border: '1px solid transparent', transition: 'all 0.2s' }} > {value || '选择日期'}
{isOpen && (
{year}年 {month + 1}月
{['日', '一', '二', '三', '四', '五', '六'].map(d => (
{d}
))} {days.map((d, i) => { if (!d) return
; const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}`; const isSelected = value === dateStr; const today = new Date(); today.setHours(0, 0, 0, 0); const current = new Date(dateStr); const isToday = current.getTime() === today.getTime(); const isFuture = current > today; return (
!isFuture && handleSelect(e, d)} style={{ height: 28, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '13px', borderRadius: '6px', cursor: isFuture ? 'not-allowed' : 'pointer', background: isSelected ? 'var(--primary)' : isToday ? 'rgba(255,255,255,0.1)' : 'transparent', color: isFuture ? 'var(--muted)' : isSelected ? '#000' : 'var(--text)', fontWeight: isSelected || isToday ? 600 : 400, opacity: isFuture ? 0.3 : 1 }} onMouseEnter={(e) => { if (!isSelected && !isFuture) e.currentTarget.style.background = 'rgba(255,255,255,0.1)'; }} onMouseLeave={(e) => { if (!isSelected && !isFuture) e.currentTarget.style.background = isToday ? 'rgba(255,255,255,0.1)' : 'transparent'; }} > {d}
); })}
)}
); } function DonateTabs() { const [method, setMethod] = useState('alipay'); // alipay, wechat return (
{method === 'alipay' ? ( 支付宝收款码 ) : ( 微信收款码 )}
); } function MinusIcon(props) { return ( ); } function NumericInput({ value, onChange, step = 1, min = 0, placeholder }) { const decimals = String(step).includes('.') ? String(step).split('.')[1].length : 0; const fmt = (n) => Number(n).toFixed(decimals); const inc = () => { const v = parseFloat(value); const base = isNaN(v) ? 0 : v; const next = base + step; onChange(fmt(next)); }; const dec = () => { const v = parseFloat(value); const base = isNaN(v) ? 0 : v; const next = Math.max(min, base - step); onChange(fmt(next)); }; return (
onChange(e.target.value)} placeholder={placeholder} style={{ width: '100%', paddingRight: 56 }} />
); } function Stat({ label, value, delta }) { const dir = delta > 0 ? 'up' : delta < 0 ? 'down' : ''; return (
{label} {value}
); } function FeedbackModal({ onClose }) { 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", "c390fbb1-77e0-4aab-a939-caa75edc7319"); formData.append("subject", "基估宝 - 用户反馈"); try { const response = await fetch("https://api.web3forms.com/submit", { method: "POST", body: formData }); const data = await response.json(); if (data.success) { setSucceeded(true); } else { setError(data.message || "提交失败,请稍后再试"); } } catch (err) { setError("网络错误,请检查您的连接"); } finally { setSubmitting(false); } }; return ( e.stopPropagation()} >
意见反馈
{succeeded ? (
🎉

感谢您的反馈!

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

) : (