feat:基金详情支持编辑持仓金额

This commit is contained in:
hzm
2026-03-10 23:15:47 +08:00
parent 1e081167b3
commit 5200b9292b
11 changed files with 339 additions and 237 deletions

View File

@@ -1,10 +1,15 @@
'use client';
import { useState, useEffect } from 'react';
import { motion } from 'framer-motion';
import { CloseIcon } from './Icons';
import { fetchSmartFundNetValue } from '../api/fund';
import { DatePicker } from './Common';
import {
Dialog,
DialogContent,
DialogTitle,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
export default function AddHistoryModal({ fund, onClose, onConfirm }) {
const [type, setType] = useState('');
@@ -77,30 +82,36 @@ export default function AddHistoryModal({ fund, onClose, onConfirm }) {
onClose();
};
const handleOpenChange = (open) => {
if (!open) {
onClose?.();
}
};
const handleCloseClick = (event) => {
event.stopPropagation();
onClose?.();
};
return (
<motion.div
className="modal-overlay"
role="dialog"
aria-modal="true"
aria-label="添加历史记录"
onClick={onClose}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
style={{ zIndex: 1200 }}
>
<motion.div
<Dialog open onOpenChange={handleOpenChange}>
<DialogContent
showCloseButton={false}
className="glass card modal"
initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: 20 }}
style={{ maxWidth: '420px' }}
onClick={(e) => e.stopPropagation()}
overlayClassName="modal-overlay"
overlayStyle={{ zIndex: 9998 }}
style={{ maxWidth: '420px', zIndex: 9999, width: '90vw' }}
>
<DialogTitle className="sr-only">添加历史记录</DialogTitle>
<div className="title" style={{ marginBottom: 20, justifyContent: 'space-between' }}>
<span>添加历史记录</span>
<button className="icon-button" onClick={onClose} style={{ border: 'none', background: 'transparent' }}>
<CloseIcon />
<button
className="icon-button"
onClick={handleCloseClick}
style={{ border: 'none', background: 'transparent' }}
>
<CloseIcon width="20" height="20" />
</button>
</div>
@@ -200,15 +211,18 @@ export default function AddHistoryModal({ fund, onClose, onConfirm }) {
<div className="muted" style={{ fontSize: '11px', lineHeight: 1.5, marginBottom: 16, paddingTop: 12, borderTop: '1px solid rgba(255,255,255,0.08)' }}>
*此处补录的买入/卖出仅作记录展示不会改变当前持仓金额与份额实际持仓请在持仓设置中维护
</div>
<button
className="button primary full-width"
onClick={handleSubmit}
disabled={!type || !date || !netValue || !amount || !share || loading}
>
确认添加
</button>
</motion.div>
</motion.div>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Button
type="button"
variant="default"
size="lg"
onClick={handleSubmit}
disabled={!type || !date || !netValue || !amount || !share || loading}
>
确认添加
</Button>
</div>
</DialogContent>
</Dialog>
);
}

View File

@@ -1,13 +1,17 @@
'use client';
import { useEffect, useState, useRef } from 'react';
import { motion } from 'framer-motion';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
import { DatePicker, NumericInput } from './Common';
import { isNumber } from 'lodash';
import { CloseIcon } from './Icons';
import {
Dialog,
DialogContent,
DialogTitle,
} from '@/components/ui/dialog';
dayjs.extend(utc);
dayjs.extend(timezone);
@@ -170,30 +174,28 @@ export default function DcaModal({ fund, plan, onClose, onConfirm }) {
return true;
};
const handleOpenChange = (open) => {
if (!open) {
onClose?.();
}
};
return (
<motion.div
className="modal-overlay"
role="dialog"
aria-modal="true"
aria-label="定投设置"
onClick={onClose}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<motion.div
initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: 20 }}
<Dialog open onOpenChange={handleOpenChange}>
<DialogContent
showCloseButton={false}
className="glass card modal dca-modal"
onClick={(e) => e.stopPropagation()}
overlayClassName="modal-overlay"
style={{
maxWidth: '420px',
maxHeight: '90vh',
display: 'flex',
flexDirection: 'column',
zIndex: 999,
width: '90vw',
}}
>
<DialogTitle className="sr-only">定投设置</DialogTitle>
<div
className="scrollbar-y-styled"
style={{
@@ -376,8 +378,8 @@ export default function DcaModal({ fund, plan, onClose, onConfirm }) {
</button>
</div>
</div>
</motion.div>
</motion.div>
</DialogContent>
</Dialog>
);
}

View File

@@ -227,25 +227,25 @@ export default function FundCard({
display: 'flex',
alignItems: 'center',
gap: 4,
cursor: layoutMode === 'drawer' ? 'default' : 'pointer',
cursor: 'pointer',
}}
onClick={() => layoutMode !== 'drawer' && onHoldingClick?.(f)}
onClick={() => onHoldingClick?.(f)}
>
未设置 {layoutMode !== 'drawer' && <SettingsIcon width="12" height="12" />}
未设置 <SettingsIcon width="12" height="12" />
</div>
</div>
) : (
<>
<div
className="stat"
style={{ cursor: layoutMode === 'drawer' ? 'default' : 'pointer', flexDirection: 'column', gap: 4 }}
onClick={() => layoutMode !== 'drawer' && onActionClick?.(f)}
style={{ cursor: 'pointer', flexDirection: 'column', gap: 4 }}
onClick={() => onActionClick?.(f)}
>
<span
className="label"
style={{ display: 'flex', alignItems: 'center', gap: 4 }}
>
持仓金额 {layoutMode !== 'drawer' && <SettingsIcon width="12" height="12" style={{ opacity: 0.7 }} />}
持仓金额 <SettingsIcon width="12" height="12" style={{ opacity: 0.7 }} />
</span>
<span className="value">
{masked ? '******' : `¥${profit.amount.toFixed(2)}`}

View File

@@ -1,53 +1,50 @@
'use client';
import { motion } from 'framer-motion';
import { CloseIcon, SettingsIcon } from './Icons';
import {
Dialog,
DialogContent,
DialogTitle,
} from '@/components/ui/dialog';
export default function HoldingActionModal({ fund, onClose, onAction, hasHistory }) {
const handleOpenChange = (open) => {
if (!open) {
onClose?.();
}
};
return (
<motion.div
className="modal-overlay"
role="dialog"
aria-modal="true"
aria-label="持仓操作"
onClick={onClose}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<motion.div
initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: 20 }}
<Dialog open onOpenChange={handleOpenChange}>
<DialogContent
showCloseButton={false}
className="glass card modal"
onClick={(e) => e.stopPropagation()}
style={{ maxWidth: '320px' }}
overlayClassName="modal-overlay"
style={{ maxWidth: '320px', zIndex: 99 }}
>
<DialogTitle className="sr-only">持仓操作</DialogTitle>
<div className="title" style={{ marginBottom: 20, justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<SettingsIcon width="20" height="20" />
<span>持仓操作</span>
<button
type="button"
onClick={() => onAction('history')}
style={{
marginLeft: 8,
padding: '4px 8px',
fontSize: '12px',
background: 'rgba(255,255,255,0.1)',
border: 'none',
borderRadius: '4px',
color: 'var(--text)',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: 4
}}
title="查看交易记录"
>
<span>📜</span>
<span>交易记录</span>
</button>
<button
type="button"
className="button secondary"
onClick={() => onAction('history')}
style={{
marginLeft: 8,
padding: '4px 10px',
fontSize: '12px',
height: '28px',
display: 'flex',
alignItems: 'center',
gap: 4,
}}
title="查看交易记录"
>
<span>📜</span>
<span>交易记录</span>
</button>
</div>
<button className="icon-button" onClick={onClose} style={{ border: 'none', background: 'transparent' }}>
<CloseIcon width="20" height="20" />
@@ -92,13 +89,13 @@ export default function HoldingActionModal({ fund, onClose, onAction, hasHistory
background: 'linear-gradient(180deg, #ef4444, #f87171)',
border: 'none',
color: '#2b0b0b',
fontWeight: 600
fontWeight: 600,
}}
>
清空持仓
</button>
</div>
</motion.div>
</motion.div>
</DialogContent>
</Dialog>
);
}

View File

@@ -1,8 +1,12 @@
'use client';
import { useEffect, useState } from 'react';
import { motion } from 'framer-motion';
import { CloseIcon, SettingsIcon } from './Icons';
import {
Dialog,
DialogContent,
DialogTitle,
} from '@/components/ui/dialog';
export default function HoldingEditModal({ fund, holding, onClose, onSave }) {
const [mode, setMode] = useState('amount'); // 'amount' | 'share'
@@ -89,25 +93,21 @@ export default function HoldingEditModal({ fund, holding, onClose, onSave }) {
? (share && cost && !isNaN(share) && !isNaN(cost))
: (amount && !isNaN(amount) && (!profit || !isNaN(profit)) && dwjz > 0);
const handleOpenChange = (open) => {
if (!open) {
onClose?.();
}
};
return (
<motion.div
className="modal-overlay"
role="dialog"
aria-modal="true"
aria-label="编辑持仓"
onClick={onClose}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<motion.div
initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: 20 }}
<Dialog open onOpenChange={handleOpenChange}>
<DialogContent
showCloseButton={false}
className="glass card modal"
onClick={(e) => e.stopPropagation()}
style={{ maxWidth: '400px' }}
overlayClassName="modal-overlay"
style={{ maxWidth: '400px', zIndex: 999, width: '90vw' }}
>
<DialogTitle className="sr-only">编辑持仓</DialogTitle>
<div className="title" style={{ marginBottom: 20, justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<SettingsIcon width="20" height="20" />
@@ -238,7 +238,7 @@ export default function HoldingEditModal({ fund, holding, onClose, onSave }) {
</button>
</div>
</form>
</motion.div>
</motion.div>
</DialogContent>
</Dialog>
);
}

View File

@@ -0,0 +1,94 @@
'use client';
import { CloseIcon } from './Icons';
import {
Dialog,
DialogContent,
DialogTitle,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
export default function PendingTradesModal({
open,
trades = [],
onClose,
onRevoke,
}) {
const handleOpenChange = (nextOpen) => {
if (!nextOpen) {
onClose?.();
}
};
return (
<Dialog open={open} onOpenChange={handleOpenChange}>
<DialogContent
showCloseButton={false}
className="glass card modal trade-modal"
overlayClassName="modal-overlay"
overlayStyle={{ zIndex: 998 }}
style={{ maxWidth: '420px', zIndex: 999, width: '90vw' }}
>
<DialogTitle className="sr-only">待交易队列</DialogTitle>
<div className="title" style={{ marginBottom: 20, justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<span style={{ fontSize: '20px' }}>📥</span>
<span>待交易队列</span>
</div>
<button
className="icon-button"
onClick={onClose}
style={{ border: 'none', background: 'transparent' }}
>
<CloseIcon width="20" height="20" />
</button>
</div>
<div className="pending-list" style={{ maxHeight: '300px', overflowY: 'auto' }}>
<div className="pending-list-items" style={{ paddingTop: 0 }}>
{trades.map((trade, idx) => (
<div key={trade.id || idx} className="trade-pending-item">
<div className="row" style={{ justifyContent: 'space-between', marginBottom: 4 }}>
<span
style={{
fontWeight: 600,
fontSize: '14px',
color: trade.type === 'buy' ? 'var(--danger)' : 'var(--success)',
}}
>
{trade.type === 'buy' ? '买入' : '卖出'}
</span>
<span className="muted" style={{ fontSize: '12px' }}>
{trade.date} {trade.isAfter3pm ? '(15:00后)' : ''}
</span>
</div>
<div className="row" style={{ justifyContent: 'space-between', fontSize: '12px' }}>
<span className="muted">份额/金额</span>
<span>{trade.share ? `${trade.share}` : `¥${trade.amount}`}</span>
</div>
<div className="row" style={{ justifyContent: 'space-between', fontSize: '12px', marginTop: 4 }}>
<span className="muted">状态</span>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<span className="trade-pending-status">等待净值更新...</span>
<Button
type="button"
size="xs"
variant="destructive"
className="bg-destructive text-white hover:bg-destructive/90"
onClick={() => onRevoke?.(trade)}
style={{ paddingInline: 10 }}
>
撤销
</Button>
</div>
</div>
</div>
))}
</div>
</div>
</DialogContent>
</Dialog>
);
}

View File

@@ -1,7 +1,7 @@
'use client';
import { useEffect, useMemo, useState } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import { AnimatePresence } from 'framer-motion';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
@@ -10,6 +10,12 @@ import { fetchSmartFundNetValue } from '../api/fund';
import { DatePicker, NumericInput } from './Common';
import ConfirmModal from './ConfirmModal';
import { CloseIcon } from './Icons';
import {
Dialog,
DialogContent,
DialogTitle,
} from '@/components/ui/dialog';
import PendingTradesModal from './PendingTradesModal';
dayjs.extend(utc);
dayjs.extend(timezone);
@@ -153,36 +159,33 @@ export default function TradeModal({ type, fund, holding, onClose, onConfirm, pe
const [revokeTrade, setRevokeTrade] = useState(null);
const handleOpenChange = (open) => {
if (!open) {
onClose?.();
}
};
return (
<motion.div
className="modal-overlay"
role="dialog"
aria-modal="true"
aria-label={isBuy ? "加仓" : "减仓"}
onClick={onClose}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<motion.div
initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: 20 }}
<Dialog open onOpenChange={handleOpenChange}>
<DialogContent
showCloseButton={false}
className="glass card modal trade-modal"
onClick={(e) => e.stopPropagation()}
style={{ maxWidth: '420px' }}
overlayClassName="modal-overlay"
overlayStyle={{ zIndex: 99 }}
style={{ maxWidth: '420px', width: '90vw', zIndex: 99 }}
>
<DialogTitle className="sr-only">{isBuy ? '加仓' : '减仓'}</DialogTitle>
<div className="title" style={{ marginBottom: 20, justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<span style={{ fontSize: '20px' }}>{isBuy ? '📥' : '📤'}</span>
<span>{showPendingList ? '待交易队列' : (showConfirm ? (isBuy ? '买入确认' : '卖出确认') : (isBuy ? '加仓' : '减仓'))}</span>
<span>{showConfirm ? (isBuy ? '买入确认' : '卖出确认') : (isBuy ? '加仓' : '减仓')}</span>
</div>
<button className="icon-button" onClick={onClose} style={{ border: 'none', background: 'transparent' }}>
<CloseIcon width="20" height="20" />
</button>
</div>
{!showPendingList && !showConfirm && currentPendingTrades.length > 0 && (
{!showConfirm && currentPendingTrades.length > 0 && (
<div
className="trade-pending-alert"
onClick={() => setShowPendingList(true)}
@@ -192,49 +195,6 @@ export default function TradeModal({ type, fund, holding, onClose, onConfirm, pe
</div>
)}
{showPendingList ? (
<div className="pending-list" style={{ maxHeight: '300px', overflowY: 'auto' }}>
<div className="pending-list-header trade-pending-header">
<button
className="button secondary"
onClick={() => setShowPendingList(false)}
style={{ padding: '4px 8px', fontSize: '12px' }}
>
&lt; 返回
</button>
</div>
<div className="pending-list-items" style={{ paddingTop: 0 }}>
{currentPendingTrades.map((trade, idx) => (
<div key={trade.id || idx} className="trade-pending-item">
<div className="row" style={{ justifyContent: 'space-between', marginBottom: 4 }}>
<span style={{ fontWeight: 600, fontSize: '14px', color: trade.type === 'buy' ? 'var(--danger)' : 'var(--success)' }}>
{trade.type === 'buy' ? '买入' : '卖出'}
</span>
<span className="muted" style={{ fontSize: '12px' }}>{trade.date} {trade.isAfter3pm ? '(15:00后)' : ''}</span>
</div>
<div className="row" style={{ justifyContent: 'space-between', fontSize: '12px' }}>
<span className="muted">份额/金额</span>
<span>{trade.share ? `${trade.share}` : `¥${trade.amount}`}</span>
</div>
<div className="row" style={{ justifyContent: 'space-between', fontSize: '12px', marginTop: 4 }}>
<span className="muted">状态</span>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<span className="trade-pending-status">等待净值更新...</span>
<button
className="button secondary trade-revoke-btn"
onClick={() => setRevokeTrade(trade)}
style={{ padding: '2px 8px', fontSize: '10px', height: 'auto' }}
>
撤销
</button>
</div>
</div>
</div>
))}
</div>
</div>
) : (
<>
{!showConfirm && (
<div style={{ marginBottom: 16 }}>
<div className="fund-name" style={{ fontWeight: 600, fontSize: '16px', marginBottom: 4 }}>{fund?.name}</div>
@@ -316,10 +276,10 @@ export default function TradeModal({ type, fund, holding, onClose, onConfirm, pe
</button>
<button
type="button"
className="button"
className="button queue-button"
onClick={handleFinalConfirm}
disabled={loadingPrice}
style={{ flex: 1, background: 'var(--primary)', opacity: loadingPrice ? 0.6 : 1, color: '#05263b' }}
style={{ flex: 1, background: 'var(--primary)', opacity: loadingPrice ? 0.6 : 1 }}
>
{loadingPrice ? '请稍候' : (price ? '确认买入' : '加入待处理队列')}
</button>
@@ -398,7 +358,7 @@ export default function TradeModal({ type, fund, holding, onClose, onConfirm, pe
</button>
<button
type="button"
className="button"
className="button queue-button"
onClick={handleFinalConfirm}
disabled={loadingPrice}
style={{ flex: 1, background: 'var(--danger)', opacity: loadingPrice ? 0.6 : 1 }}
@@ -612,9 +572,7 @@ export default function TradeModal({ type, fund, holding, onClose, onConfirm, pe
</div>
</form>
)}
</>
)}
</motion.div>
</DialogContent>
<AnimatePresence>
{revokeTrade && (
<ConfirmModal
@@ -630,6 +588,12 @@ export default function TradeModal({ type, fund, holding, onClose, onConfirm, pe
/>
)}
</AnimatePresence>
</motion.div>
<PendingTradesModal
open={showPendingList}
trades={currentPendingTrades}
onClose={() => setShowPendingList(false)}
onRevoke={(trade) => setRevokeTrade(trade)}
/>
</Dialog>
);
}

View File

@@ -1,18 +1,24 @@
'use client';
import { useState, useMemo } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { AnimatePresence } from 'framer-motion';
import { CloseIcon } from './Icons';
import ConfirmModal from './ConfirmModal';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogContent,
DialogTitle,
} from '@/components/ui/dialog';
export default function TransactionHistoryModal({
fund,
transactions = [],
pendingTransactions = [],
onClose,
export default function TransactionHistoryModal({
fund,
transactions = [],
pendingTransactions = [],
onClose,
onDeleteTransaction,
onDeletePending,
onAddHistory
onAddHistory,
}) {
const [deleteConfirm, setDeleteConfirm] = useState(null); // { type: 'pending' | 'history', item }
@@ -39,31 +45,46 @@ export default function TransactionHistoryModal({
setDeleteConfirm(null);
};
const handleCloseClick = (event) => {
// 只关闭交易记录弹框,避免事件冒泡影响到其他弹框(例如 HoldingActionModal
event.stopPropagation();
onClose?.();
};
const handleOpenChange = (open) => {
if (!open) {
onClose?.();
}
};
return (
<motion.div
className="modal-overlay"
role="dialog"
aria-modal="true"
onClick={onClose}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
style={{ zIndex: 1100 }} // Higher than TradeModal if stacked, but usually TradeModal closes or this opens on top
>
<motion.div
initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: 20 }}
<Dialog open onOpenChange={handleOpenChange}>
<DialogContent
showCloseButton={false}
className="glass card modal tx-history-modal"
onClick={(e) => e.stopPropagation()}
style={{ maxWidth: '480px', maxHeight: '80vh', display: 'flex', flexDirection: 'column' }}
overlayClassName="modal-overlay"
overlayStyle={{ zIndex: 998 }}
style={{
maxWidth: '480px',
width: '90vw',
maxHeight: '80vh',
display: 'flex',
flexDirection: 'column',
zIndex: 999, // 保持原有层级,确保在其他弹框之上
}}
>
<DialogTitle className="sr-only">交易记录</DialogTitle>
<div className="title" style={{ marginBottom: 20, justifyContent: 'space-between', flexShrink: 0 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<span style={{ fontSize: '20px' }}>📜</span>
<span>交易记录</span>
</div>
<button className="icon-button" onClick={onClose} style={{ border: 'none', background: 'transparent' }}>
<button
className="icon-button"
onClick={handleCloseClick}
style={{ border: 'none', background: 'transparent' }}
>
<CloseIcon width="20" height="20" />
</button>
</div>
@@ -73,10 +94,10 @@ export default function TransactionHistoryModal({
<div className="fund-name" style={{ fontWeight: 600, fontSize: '16px', marginBottom: 4 }}>{fund?.name}</div>
<div className="muted" style={{ fontSize: '12px' }}>#{fund?.code}</div>
</div>
<button
className="button primary"
<button
className="button primary"
onClick={onAddHistory}
style={{ fontSize: '12px', padding: '4px 12px', height: 'auto' }}
style={{ fontSize: '12px', padding: '4px 12px', height: 'auto', width: '80px' }}
>
添加记录
</button>
@@ -108,13 +129,16 @@ export default function TransactionHistoryModal({
</div>
<div className="row" style={{ justifyContent: 'space-between', fontSize: '12px', marginTop: 8 }}>
<span className="tx-history-pending-status">等待净值更新...</span>
<button
className="button secondary tx-history-action-btn"
<Button
type="button"
size="xs"
variant="destructive"
className="bg-destructive text-white hover:bg-destructive/90"
onClick={() => handleDeleteClick(item, 'pending')}
style={{ padding: '2px 8px', fontSize: '10px', height: 'auto' }}
style={{ paddingInline: 10 }}
>
撤销
</button>
</Button>
</div>
</div>
))}
@@ -158,13 +182,16 @@ export default function TransactionHistoryModal({
)}
<div className="row" style={{ justifyContent: 'space-between', fontSize: '12px', marginTop: 8 }}>
<span className="muted"></span>
<button
className="button secondary tx-history-action-btn"
<Button
type="button"
size="xs"
variant="destructive"
className="bg-destructive text-white hover:bg-destructive/90"
onClick={() => handleDeleteClick(item, 'history')}
style={{ padding: '2px 8px', fontSize: '10px', height: 'auto' }}
style={{ paddingInline: 10 }}
>
删除记录
</button>
</Button>
</div>
</div>
))
@@ -172,22 +199,21 @@ export default function TransactionHistoryModal({
</div>
</div>
</motion.div>
<AnimatePresence>
{deleteConfirm && (
<ConfirmModal
key="delete-confirm"
title={deleteConfirm.type === 'pending' ? "撤销交易" : "删除记录"}
message={deleteConfirm.type === 'pending'
? "确定要撤销这笔待处理交易吗?"
: "确定要删除这条交易记录吗?\n注意删除记录不会恢复已变更的持仓数据。"}
onConfirm={handleConfirmDelete}
onCancel={() => setDeleteConfirm(null)}
confirmText="确认删除"
/>
)}
</AnimatePresence>
</motion.div>
<AnimatePresence>
{deleteConfirm && (
<ConfirmModal
key="delete-confirm"
title={deleteConfirm.type === 'pending' ? '撤销交易' : '删除记录'}
message={deleteConfirm.type === 'pending'
? '确定要撤销这笔待处理交易吗?'
: '确定要删除这条交易记录吗?\n注意删除记录不会恢复已变更的持仓数据。'}
onConfirm={handleConfirmDelete}
onCancel={() => setDeleteConfirm(null)}
confirmText="确认删除"
/>
)}
</AnimatePresence>
</DialogContent>
</Dialog>
);
}

View File

@@ -2270,6 +2270,10 @@ input[type="number"] {
color: var(--text) !important;
}
[data-theme="light"] .trade-modal .queue-button {
color: #ffffff !important;
}
.trade-time-slot {
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;

View File

@@ -9,7 +9,7 @@ const buttonVariants = cva(
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
default: "bg-[linear-gradient(#0ea5e9,#0891b2)] text-white hover:bg-[linear-gradient(#0284c7,#0e7490)]",
destructive:
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
outline:

View File

@@ -50,11 +50,12 @@ function DialogContent({
children,
showCloseButton = true,
overlayClassName,
overlayStyle,
...props
}) {
return (
<DialogPortal data-slot="dialog-portal">
<DialogOverlay className={overlayClassName} />
<DialogOverlay className={overlayClassName} style={overlayStyle} />
<DialogPrimitive.Content
data-slot="dialog-content"
className={cn(