Open signature in a landscape overlay and fix mobile touch
The inline signature pad on the confirm page was unusable on phones: the canvas had a fixed 480px internal width but shrank via max-width on small screens, so pointer coordinates landed in only the top-left fraction of the drawing buffer. Edge-of-screen strokes also collided with iOS Safari back-swipe. Tapping the new signature trigger now opens a full-screen overlay that rotates to landscape (via 100dvh/100dvw + CSS rotate on portrait phones, plus an opportunistic screen.orientation.lock) so customers get the widest possible signing area, away from the system edge. Also swap the hand-rolled drawing logic for signature_pad, with a ResizeObserver-driven resize that preserves strokes via toData/fromData and scales the canvas to devicePixelRatio. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { Card, Button, Spin, Result, message, Modal, Input } from 'antd';
|
||||
import {
|
||||
@@ -6,11 +6,12 @@ import {
|
||||
CloseCircleOutlined,
|
||||
ClockCircleOutlined,
|
||||
ExclamationCircleOutlined,
|
||||
EditOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { aftersalesApi } from '@/services/api';
|
||||
import type { AftersalesPublicView, AftersalesServiceType } from '@/types';
|
||||
import PublicLayout, { PublicLogo } from '@/components/PublicLayout';
|
||||
import SignaturePad, { type SignaturePadHandle } from '@/components/SignaturePad';
|
||||
import SignatureOverlay from '@/components/SignatureOverlay';
|
||||
import './styles/PublicQuery.css';
|
||||
import './styles/AftersalesConfirm.css';
|
||||
|
||||
@@ -29,8 +30,7 @@ function AftersalesConfirmPage() {
|
||||
const [rejectReason, setRejectReason] = useState('');
|
||||
const [showRejectDialog, setShowRejectDialog] = useState(false);
|
||||
const [signatureData, setSignatureData] = useState('');
|
||||
|
||||
const signatureRef = useRef<SignaturePadHandle>(null);
|
||||
const [showSignatureOverlay, setShowSignatureOverlay] = useState(false);
|
||||
|
||||
const loadOrder = async () => {
|
||||
setLoading(true);
|
||||
@@ -53,7 +53,7 @@ function AftersalesConfirmPage() {
|
||||
}, [serialNumber]);
|
||||
|
||||
const handleAuthorize = async () => {
|
||||
if (signatureRef.current?.isEmpty() || !signatureData) {
|
||||
if (!signatureData) {
|
||||
message.error('请先签名');
|
||||
return;
|
||||
}
|
||||
@@ -100,7 +100,6 @@ function AftersalesConfirmPage() {
|
||||
};
|
||||
|
||||
const handleClearSignature = () => {
|
||||
signatureRef.current?.clear();
|
||||
setSignatureData('');
|
||||
};
|
||||
|
||||
@@ -236,14 +235,33 @@ function AftersalesConfirmPage() {
|
||||
<>
|
||||
<div className="aftersales-signature-section">
|
||||
<div className="aftersales-signature-header">
|
||||
<p className="aftersales-signature-tip">请在下方签名确认维修结果</p>
|
||||
<Button size="small" type="link" onClick={handleClearSignature}>
|
||||
清除签名
|
||||
</Button>
|
||||
</div>
|
||||
<div className="aftersales-signature-canvas-wrap">
|
||||
<SignaturePad ref={signatureRef} onChange={setSignatureData} />
|
||||
<p className="aftersales-signature-tip">请签名确认维修结果</p>
|
||||
{signatureData && (
|
||||
<Button size="small" type="link" onClick={handleClearSignature}>
|
||||
清除签名
|
||||
</Button>
|
||||
)}
|
||||
</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
|
||||
@@ -269,6 +287,15 @@ function AftersalesConfirmPage() {
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<SignatureOverlay
|
||||
open={showSignatureOverlay}
|
||||
onCancel={() => setShowSignatureOverlay(false)}
|
||||
onConfirm={(url) => {
|
||||
setSignatureData(url);
|
||||
setShowSignatureOverlay(false);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
title="请说明退回原因"
|
||||
open={showRejectDialog}
|
||||
|
||||
@@ -51,23 +51,64 @@
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.aftersales-signature-canvas-wrap {
|
||||
border: 1px dashed #94a3b8;
|
||||
.aftersales-signature-trigger {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
padding: 22px 16px;
|
||||
border: 1.5px dashed #94a3b8;
|
||||
border-radius: 12px;
|
||||
background: #f8fafc;
|
||||
color: #1f2937;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
transition: background 0.15s, border-color 0.15s;
|
||||
}
|
||||
|
||||
.aftersales-signature-trigger:active {
|
||||
background: #eef2f7;
|
||||
border-color: #1677ff;
|
||||
}
|
||||
|
||||
.aftersales-signature-trigger .anticon {
|
||||
font-size: 22px;
|
||||
color: #1677ff;
|
||||
}
|
||||
|
||||
.aftersales-signature-trigger small {
|
||||
color: #6b7280;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.aftersales-signature-preview {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 10px;
|
||||
background: #ffffff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
touch-action: none;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.signature-pad-canvas {
|
||||
display: block;
|
||||
.aftersales-signature-preview img {
|
||||
max-width: 100%;
|
||||
touch-action: none;
|
||||
background: #ffffff;
|
||||
border-radius: 6px;
|
||||
cursor: crosshair;
|
||||
max-height: 160px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.aftersales-signature-preview-hint {
|
||||
font-size: 12px;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.aftersales-signature-archived {
|
||||
|
||||
Reference in New Issue
Block a user