Require dual signatures for aftersales confirmation

This commit is contained in:
Frudrax Cheng
2026-06-02 10:38:40 +08:00
parent d216a25364
commit 30e3ac67d2
6 changed files with 205 additions and 74 deletions
+121 -45
View File
@@ -29,8 +29,11 @@ function AftersalesConfirmPage() {
const [submitting, setSubmitting] = useState(false);
const [rejectReason, setRejectReason] = useState('');
const [showRejectDialog, setShowRejectDialog] = useState(false);
const [signatureData, setSignatureData] = useState('');
const [showSignatureOverlay, setShowSignatureOverlay] = useState(false);
const [customerSignatureData, setCustomerSignatureData] = useState('');
const [responsibleSignatureData, setResponsibleSignatureData] = useState('');
const [activeSignatureRole, setActiveSignatureRole] = useState<'customer' | 'responsible' | null>(
null,
);
const loadOrder = async () => {
setLoading(true);
@@ -53,15 +56,20 @@ function AftersalesConfirmPage() {
}, [serialNumber]);
const handleAuthorize = async () => {
if (!signatureData) {
message.error('请先签名');
if (!customerSignatureData) {
message.error('请先完成客户签名');
return;
}
if (!responsibleSignatureData) {
message.error('请先完成负责人签名');
return;
}
setSubmitting(true);
try {
const updated = await aftersalesApi.customerConfirm(serialNumber, {
action: 'authorize',
signature: signatureData,
signature: customerSignatureData,
responsibleSignature: responsibleSignatureData,
});
setOrder(updated);
message.success('感谢您的确认,工单已完成');
@@ -99,8 +107,16 @@ function AftersalesConfirmPage() {
}
};
const handleClearSignature = () => {
setSignatureData('');
const handleClearSignature = (role: 'customer' | 'responsible') => {
if (role === 'customer') {
setCustomerSignatureData('');
return;
}
setResponsibleSignatureData('');
};
const openSignatureOverlay = (role: 'customer' | 'responsible') => {
setActiveSignatureRole(role);
};
if (loading) {
@@ -220,48 +236,103 @@ function AftersalesConfirmPage() {
</div>
</div>
{isClosed && order.signature && (
{isClosed && (order.signature || order.responsibleSignature) && (
<div className="aftersales-signature-archived">
<p className="aftersales-signature-tip"></p>
<img
src={order.signature}
alt="客户确认签名"
className="aftersales-signature-archived-img"
/>
<div className="aftersales-signature-grid">
{order.signature && (
<div className="aftersales-signature-archived-item">
<p className="aftersales-signature-tip"></p>
<img
src={order.signature}
alt="客户签名"
className="aftersales-signature-archived-img"
/>
</div>
)}
{order.responsibleSignature && (
<div className="aftersales-signature-archived-item">
<p className="aftersales-signature-tip"></p>
<img
src={order.responsibleSignature}
alt="负责人签名"
className="aftersales-signature-archived-img"
/>
</div>
)}
</div>
</div>
)}
{isPending && (
<>
<div className="aftersales-signature-section">
<div className="aftersales-signature-header">
<p className="aftersales-signature-tip"></p>
{signatureData && (
<Button size="small" type="link" onClick={handleClearSignature}>
</Button>
)}
<p className="aftersales-signature-section-title"></p>
<div className="aftersales-signature-grid">
<div className="aftersales-signature-item">
<div className="aftersales-signature-header">
<p className="aftersales-signature-tip"></p>
{customerSignatureData && (
<Button size="small" type="link" onClick={() => handleClearSignature('customer')}>
</Button>
)}
</div>
{customerSignatureData ? (
<button
type="button"
className="aftersales-signature-preview"
onClick={() => openSignatureOverlay('customer')}
>
<img src={customerSignatureData} alt="客户签名" />
<span className="aftersales-signature-preview-hint"></span>
</button>
) : (
<button
type="button"
className="aftersales-signature-trigger"
onClick={() => openSignatureOverlay('customer')}
>
<EditOutlined />
<span></span>
<small></small>
</button>
)}
</div>
<div className="aftersales-signature-item">
<div className="aftersales-signature-header">
<p className="aftersales-signature-tip"></p>
{responsibleSignatureData && (
<Button
size="small"
type="link"
onClick={() => handleClearSignature('responsible')}
>
</Button>
)}
</div>
{responsibleSignatureData ? (
<button
type="button"
className="aftersales-signature-preview"
onClick={() => openSignatureOverlay('responsible')}
>
<img src={responsibleSignatureData} alt="负责人签名" />
<span className="aftersales-signature-preview-hint"></span>
</button>
) : (
<button
type="button"
className="aftersales-signature-trigger"
onClick={() => openSignatureOverlay('responsible')}
>
<EditOutlined />
<span></span>
<small></small>
</button>
)}
</div>
</div>
{signatureData ? (
<button
type="button"
className="aftersales-signature-preview"
onClick={() => setShowSignatureOverlay(true)}
>
<img src={signatureData} alt="客户确认签名" />
<span className="aftersales-signature-preview-hint"></span>
</button>
) : (
<button
type="button"
className="aftersales-signature-trigger"
onClick={() => setShowSignatureOverlay(true)}
>
<EditOutlined />
<span></span>
<small></small>
</button>
)}
</div>
<div className="aftersales-actions">
<Button
@@ -288,11 +359,16 @@ function AftersalesConfirmPage() {
</Card>
<SignatureOverlay
open={showSignatureOverlay}
onCancel={() => setShowSignatureOverlay(false)}
open={activeSignatureRole !== null}
title={activeSignatureRole === 'responsible' ? '负责人签名' : '客户签名'}
onCancel={() => setActiveSignatureRole(null)}
onConfirm={(url) => {
setSignatureData(url);
setShowSignatureOverlay(false);
if (activeSignatureRole === 'responsible') {
setResponsibleSignatureData(url);
} else {
setCustomerSignatureData(url);
}
setActiveSignatureRole(null);
}}
/>