Add aftersales electronic order form

This commit is contained in:
Frudrax Cheng
2026-05-29 09:53:46 +08:00
parent 78cec2c65d
commit 6067e8621a
2 changed files with 232 additions and 0 deletions
+144
View File
@@ -17,6 +17,8 @@ import {
} from 'antd';
import {
ArrowLeftOutlined,
FileTextOutlined,
PrinterOutlined,
QrcodeOutlined,
SaveOutlined,
SendOutlined,
@@ -24,6 +26,7 @@ import {
UserSwitchOutlined,
} from '@ant-design/icons';
import { aftersalesApi, authApi, usersApi } from '@/services/api';
import logo from '@/assets/img/logo.png?url';
import type {
AftersalesOrder,
AftersalesServiceType,
@@ -31,6 +34,7 @@ import type {
UpdateAftersalesRequest,
User,
} from '@/types';
import './styles/AftersalesDetail.css';
const SERVICE_TYPE_LABEL: Record<AftersalesServiceType, string> = {
software: '软件故障',
@@ -52,6 +56,12 @@ const WORK_ORDER_STATUS_COLOR: Record<AftersalesWorkOrderStatus, string> = {
rejected: 'warning',
};
const AUTHORIZATION_STATUS_LABEL = {
pending: '待确认',
authorized: '已授权',
unauthorized: '未授权',
} as const;
function statusStepIndex(status: AftersalesWorkOrderStatus): number {
switch (status) {
case 'created':
@@ -64,6 +74,11 @@ function statusStepIndex(status: AftersalesWorkOrderStatus): number {
}
}
function formatDateTime(value?: string) {
if (!value) return '-';
return new Date(value).toLocaleString('zh-CN');
}
function AftersalesDetailPage() {
const { serialNumber = '' } = useParams<{ serialNumber: string }>();
const navigate = useNavigate();
@@ -79,6 +94,7 @@ function AftersalesDetailPage() {
const [qrModalVisible, setQrModalVisible] = useState(false);
const [qrCodeDataUrl, setQrCodeDataUrl] = useState('');
const [qrUrl, setQrUrl] = useState('');
const [electronicFormVisible, setElectronicFormVisible] = useState(false);
const [reassignModalVisible, setReassignModalVisible] = useState(false);
const [reassignTechnicianId, setReassignTechnicianId] = useState<number | undefined>();
@@ -185,6 +201,44 @@ function AftersalesDetailPage() {
link.click();
};
const handlePrintElectronicForm = () => {
const formNode = document.querySelector('.aftersales-electronic-form');
if (!formNode) return;
const printWindow = window.open('', '_blank', 'width=960,height=720');
if (!printWindow) {
message.error('无法打开打印窗口,请检查浏览器弹窗设置');
return;
}
printWindow.document.write(`
<!doctype html>
<html>
<head>
<title>${order?.serialNumber || '售后电子表单'}</title>
<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-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-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; }
.electronic-form-table td { min-height: 24px; }
.electronic-form-table .electronic-form-code { color: #165dff; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 16px; font-weight: 700; }
.electronic-form-text { min-height: 72px; white-space: pre-wrap; }
</style>
</head>
<body>${formNode.outerHTML}</body>
</html>
`);
printWindow.document.close();
printWindow.focus();
setTimeout(() => printWindow.print(), 300);
};
const handleForceClose = () => {
if (!order) return;
Modal.confirm({
@@ -269,6 +323,9 @@ function AftersalesDetailPage() {
}
extra={
<Space>
<Button icon={<FileTextOutlined />} onClick={() => setElectronicFormVisible(true)}>
</Button>
<Button icon={<QrcodeOutlined />} onClick={handleGenerateQrCode}>
</Button>
@@ -462,6 +519,93 @@ function AftersalesDetailPage() {
</div>
</Modal>
<Modal
title="售后电子表单"
open={electronicFormVisible}
onCancel={() => setElectronicFormVisible(false)}
width={960}
footer={[
<Button key="print" icon={<PrinterOutlined />} onClick={handlePrintElectronicForm}>
</Button>,
<Button key="close" type="primary" onClick={() => setElectronicFormVisible(false)}>
</Button>,
]}
>
<div className="aftersales-electronic-form">
<div className="electronic-form-header">
<img src={logo} alt="浙江贝凡" className="electronic-form-logo" />
<div className="electronic-form-title"></div>
<div className="electronic-form-serial">{order.serialNumber}</div>
</div>
<table className="electronic-form-table">
<tbody>
<tr>
<th></th>
<td className="electronic-form-code">{order.serialNumber}</td>
<th></th>
<td>{WORK_ORDER_STATUS_LABEL[order.workOrderStatus]}</td>
</tr>
<tr>
<th></th>
<td>{AUTHORIZATION_STATUS_LABEL[order.authorizationStatus]}</td>
<th></th>
<td>{SERVICE_TYPE_LABEL[order.serviceType]}</td>
</tr>
<tr>
<th></th>
<td>{order.companyName}</td>
<th></th>
<td>{order.companyAddress}</td>
</tr>
<tr>
<th></th>
<td>{order.contactName}</td>
<th></th>
<td>{order.contactPhone}</td>
</tr>
<tr>
<th></th>
<td>{order.technician?.name || '-'}</td>
<th></th>
<td>{order.creator?.name || '-'}</td>
</tr>
<tr>
<th></th>
<td>{formatDateTime(order.createdAt)}</td>
<th></th>
<td>{formatDateTime(order.updatedAt)}</td>
</tr>
<tr>
<th></th>
<td>{formatDateTime(order.scannedAt)}</td>
<th></th>
<td>{formatDateTime(order.confirmedAt)}</td>
</tr>
<tr>
<th>退</th>
<td>{order.rejectCount}</td>
<th></th>
<td>{formatDateTime(new Date().toISOString())}</td>
</tr>
<tr>
<th></th>
<td colSpan={3} className="electronic-form-text">
{order.issueDescription || '-'}
</td>
</tr>
<tr>
<th></th>
<td colSpan={3} className="electronic-form-text">
{order.resolutionNote || '-'}
</td>
</tr>
</tbody>
</table>
</div>
</Modal>
<Modal
title="工单分配"
open={reassignModalVisible}