Add aftersales electronic order form
This commit is contained in:
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user