Files
sub2apipay/src/components/admin/RefundDialog.tsx
erio d43b04cb5c fix: 前端暗色模式补全、Unicode 可读化、UI 优化 12 项
- PaymentQRCode 13 处 Unicode 转义替换为可读中文
- RefundDialog 完整暗色模式 + Escape 键关闭
- PayResult 页面添加暗色模式支持
- OrderStatus 使用 dark prop 调整样式
- PaymentForm 选中态暗色对比度修复
- OrderDetail 英文标签改中文 + Escape 键
- Pay 页面错误提示暗色适配
- 倒计时最后 60 秒脉动提醒
- 全局 CSS 添加中文字体栈
- MobileOrderList HTML 实体替换

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 04:16:01 +08:00

131 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useState, useEffect } from 'react';
interface RefundDialogProps {
orderId: string;
amount: number;
onConfirm: (reason: string, force: boolean) => Promise<void>;
onCancel: () => void;
warning?: string;
requireForce?: boolean;
dark?: boolean;
}
export default function RefundDialog({
orderId,
amount,
onConfirm,
onCancel,
warning,
requireForce,
dark = false,
}: RefundDialogProps) {
const [reason, setReason] = useState('');
const [force, setForce] = useState(false);
const [loading, setLoading] = useState(false);
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') onCancel();
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, [onCancel]);
const handleConfirm = async () => {
setLoading(true);
try {
await onConfirm(reason, force);
} finally {
setLoading(false);
}
};
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50" onClick={onCancel}>
<div
className={[
'w-full max-w-md rounded-xl p-6 shadow-xl',
dark ? 'bg-slate-900' : 'bg-white',
].join(' ')}
onClick={(e) => e.stopPropagation()}
>
<h3 className={['text-lg font-bold', dark ? 'text-slate-100' : 'text-gray-900'].join(' ')}>退</h3>
<div className="mt-4 space-y-3">
<div className={['rounded-lg p-3', dark ? 'bg-slate-800' : 'bg-gray-50'].join(' ')}>
<div className={['text-sm', dark ? 'text-slate-400' : 'text-gray-500'].join(' ')}></div>
<div className="text-sm font-mono">{orderId}</div>
</div>
<div className={['rounded-lg p-3', dark ? 'bg-slate-800' : 'bg-gray-50'].join(' ')}>
<div className={['text-sm', dark ? 'text-slate-400' : 'text-gray-500'].join(' ')}>退</div>
<div className="text-lg font-bold text-red-600">¥{amount.toFixed(2)}</div>
</div>
{warning && (
<div
className={[
'rounded-lg p-3 text-sm',
dark ? 'bg-yellow-900/30 text-yellow-300' : 'bg-yellow-50 text-yellow-700',
].join(' ')}
>
{warning}
</div>
)}
<div>
<label className={['mb-1 block text-sm font-medium', dark ? 'text-slate-300' : 'text-gray-700'].join(' ')}>
退
</label>
<input
type="text"
value={reason}
onChange={(e) => setReason(e.target.value)}
placeholder="请输入退款原因(可选)"
className={[
'w-full rounded-lg border px-3 py-2 text-sm focus:border-blue-500 focus:outline-none',
dark ? 'border-slate-600 bg-slate-800 text-slate-100' : 'border-gray-300 bg-white text-gray-900',
].join(' ')}
/>
</div>
{requireForce && (
<label className="flex items-center gap-2 text-sm">
<input
type="checkbox"
checked={force}
onChange={(e) => setForce(e.target.checked)}
className={['rounded', dark ? 'border-slate-600' : 'border-gray-300'].join(' ')}
/>
<span className="text-red-600">退</span>
</label>
)}
</div>
<div className="mt-6 flex gap-3">
<button
onClick={onCancel}
className={[
'flex-1 rounded-lg border py-2 text-sm',
dark
? 'border-slate-600 text-slate-300 hover:bg-slate-800'
: 'border-gray-300 text-gray-600 hover:bg-gray-50',
].join(' ')}
>
</button>
<button
onClick={handleConfirm}
disabled={loading || (requireForce && !force)}
className="flex-1 rounded-lg bg-red-600 py-2 text-sm font-medium text-white hover:bg-red-700 disabled:cursor-not-allowed disabled:bg-gray-300"
>
{loading ? '处理中...' : '确认退款'}
</button>
</div>
</div>
</div>
);
}