Add QR code to aftersales electronic form
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import QRCode from 'qrcode';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
Card,
|
||||
@@ -79,6 +80,10 @@ function formatDateTime(value?: string) {
|
||||
return new Date(value).toLocaleString('zh-CN');
|
||||
}
|
||||
|
||||
function getAftersalesPublicUrl(serialNumber: string) {
|
||||
return `${window.location.origin}/aftersales/${serialNumber}`;
|
||||
}
|
||||
|
||||
function AftersalesDetailPage() {
|
||||
const { serialNumber = '' } = useParams<{ serialNumber: string }>();
|
||||
const navigate = useNavigate();
|
||||
@@ -95,6 +100,7 @@ function AftersalesDetailPage() {
|
||||
const [qrCodeDataUrl, setQrCodeDataUrl] = useState('');
|
||||
const [qrUrl, setQrUrl] = useState('');
|
||||
const [electronicFormVisible, setElectronicFormVisible] = useState(false);
|
||||
const [electronicFormQrCodeDataUrl, setElectronicFormQrCodeDataUrl] = useState('');
|
||||
|
||||
const [reassignModalVisible, setReassignModalVisible] = useState(false);
|
||||
const [reassignTechnicianId, setReassignTechnicianId] = useState<number | undefined>();
|
||||
@@ -201,6 +207,20 @@ function AftersalesDetailPage() {
|
||||
link.click();
|
||||
};
|
||||
|
||||
const openElectronicForm = async () => {
|
||||
if (!order) return;
|
||||
try {
|
||||
const qrCode = await QRCode.toDataURL(getAftersalesPublicUrl(order.serialNumber), {
|
||||
width: 132,
|
||||
margin: 1,
|
||||
});
|
||||
setElectronicFormQrCodeDataUrl(qrCode);
|
||||
setElectronicFormVisible(true);
|
||||
} catch (err: any) {
|
||||
message.error(err?.message || '生成电子表单二维码失败');
|
||||
}
|
||||
};
|
||||
|
||||
const handlePrintElectronicForm = () => {
|
||||
const formNode = document.querySelector('.aftersales-electronic-form');
|
||||
if (!formNode) return;
|
||||
@@ -219,10 +239,15 @@ function AftersalesDetailPage() {
|
||||
<style>
|
||||
body { margin: 24px; color: #111827; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
|
||||
.aftersales-electronic-form { max-width: 1080px; margin: 0 auto; }
|
||||
.electronic-form-header { display: flex; align-items: center; justify-content: space-between; gap: 20px; margin-bottom: 16px; }
|
||||
.electronic-form-header { display: grid; grid-template-columns: minmax(180px, 1fr) auto minmax(180px, 1fr); align-items: center; gap: 20px; margin-bottom: 16px; }
|
||||
.electronic-form-brand { justify-self: start; }
|
||||
.electronic-form-logo { height: 34px; object-fit: contain; }
|
||||
.electronic-form-title { flex: 1; text-align: center; font-size: 20px; font-weight: 700; }
|
||||
.electronic-form-serial { border: 2px solid #111827; padding: 8px 12px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 16px; font-weight: 700; }
|
||||
.electronic-form-title { justify-self: center; text-align: center; font-size: 20px; font-weight: 700; }
|
||||
.electronic-form-meta { justify-self: end; display: flex; align-items: center; gap: 12px; }
|
||||
.electronic-form-qr { width: 82px; height: 82px; object-fit: contain; }
|
||||
.electronic-form-serial { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; }
|
||||
.electronic-form-serial-label { font-size: 12px; color: #4b5563; }
|
||||
.electronic-form-serial-code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 16px; font-weight: 700; white-space: nowrap; }
|
||||
.electronic-form-table { width: 100%; border-collapse: collapse; table-layout: fixed; font-size: 13px; }
|
||||
.electronic-form-table th, .electronic-form-table td { border: 1px solid #1f2937; padding: 9px 10px; vertical-align: top; word-break: break-word; }
|
||||
.electronic-form-table th { width: 120px; background: #f3f4f6; text-align: left; font-weight: 600; }
|
||||
@@ -323,7 +348,7 @@ function AftersalesDetailPage() {
|
||||
}
|
||||
extra={
|
||||
<Space>
|
||||
<Button icon={<FileTextOutlined />} onClick={() => setElectronicFormVisible(true)}>
|
||||
<Button icon={<FileTextOutlined />} onClick={openElectronicForm}>
|
||||
电子表单
|
||||
</Button>
|
||||
<Button icon={<QrcodeOutlined />} onClick={handleGenerateQrCode}>
|
||||
@@ -535,9 +560,23 @@ function AftersalesDetailPage() {
|
||||
>
|
||||
<div className="aftersales-electronic-form">
|
||||
<div className="electronic-form-header">
|
||||
<div className="electronic-form-brand">
|
||||
<img src={logo} alt="浙江贝凡" className="electronic-form-logo" />
|
||||
</div>
|
||||
<div className="electronic-form-title">浙江贝凡售后服务电子表单</div>
|
||||
<div className="electronic-form-serial">售后码:{order.serialNumber}</div>
|
||||
<div className="electronic-form-meta">
|
||||
{electronicFormQrCodeDataUrl && (
|
||||
<img
|
||||
src={electronicFormQrCodeDataUrl}
|
||||
alt="售后码二维码"
|
||||
className="electronic-form-qr"
|
||||
/>
|
||||
)}
|
||||
<div className="electronic-form-serial">
|
||||
<span className="electronic-form-serial-label">售后码</span>
|
||||
<strong className="electronic-form-serial-code">{order.serialNumber}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<table className="electronic-form-table">
|
||||
<tbody>
|
||||
|
||||
@@ -3,28 +3,55 @@
|
||||
}
|
||||
|
||||
.electronic-form-header {
|
||||
display: flex;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(180px, 1fr) auto minmax(180px, 1fr);
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.electronic-form-brand {
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
.electronic-form-logo {
|
||||
height: 34px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.electronic-form-title {
|
||||
flex: 1;
|
||||
justify-self: center;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.electronic-form-meta {
|
||||
justify-self: end;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.electronic-form-qr {
|
||||
width: 82px;
|
||||
height: 82px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.electronic-form-serial {
|
||||
border: 2px solid #111827;
|
||||
padding: 8px 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.electronic-form-serial-label {
|
||||
font-size: 12px;
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
.electronic-form-serial-code {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
@@ -68,10 +95,16 @@
|
||||
@media (max-width: 720px) {
|
||||
.electronic-form-header {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.electronic-form-brand,
|
||||
.electronic-form-title,
|
||||
.electronic-form-meta {
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
.electronic-form-title {
|
||||
text-align: left;
|
||||
font-size: 18px;
|
||||
|
||||
Reference in New Issue
Block a user