Upload site images during aftersales confirmation
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
ClockCircleOutlined,
|
||||
ExclamationCircleOutlined,
|
||||
EditOutlined,
|
||||
UploadOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { aftersalesApi } from '@/services/api';
|
||||
import type { AftersalesPublicView, AftersalesServiceType } from '@/types';
|
||||
@@ -34,6 +35,7 @@ function AftersalesConfirmPage() {
|
||||
const [activeSignatureRole, setActiveSignatureRole] = useState<'customer' | 'responsible' | null>(
|
||||
null,
|
||||
);
|
||||
const [uploadingImages, setUploadingImages] = useState(false);
|
||||
|
||||
const loadOrder = async () => {
|
||||
setLoading(true);
|
||||
@@ -119,6 +121,20 @@ function AftersalesConfirmPage() {
|
||||
setActiveSignatureRole(role);
|
||||
};
|
||||
|
||||
const handleUploadSiteImages = async (files: FileList | null) => {
|
||||
if (!files || files.length === 0) return;
|
||||
setUploadingImages(true);
|
||||
try {
|
||||
const images = await aftersalesApi.uploadSiteImages(serialNumber, Array.from(files));
|
||||
setOrder((prev) => (prev ? { ...prev, siteImages: images } : prev));
|
||||
message.success('现场图片上传成功');
|
||||
} catch (err: any) {
|
||||
message.error(err?.response?.data?.message || err.message || '上传现场图片失败');
|
||||
} finally {
|
||||
setUploadingImages(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<PublicLayout>
|
||||
@@ -224,6 +240,18 @@ function AftersalesConfirmPage() {
|
||||
<span className="value value-block">{order.resolutionNote}</span>
|
||||
</div>
|
||||
)}
|
||||
{order.siteImages && order.siteImages.length > 0 && (
|
||||
<div className="detail-item detail-item-block">
|
||||
<span className="label">现场图片</span>
|
||||
<div className="aftersales-site-images">
|
||||
{order.siteImages.map((url) => (
|
||||
<a key={url} href={url} target="_blank" rel="noreferrer">
|
||||
<img src={url} alt="现场图片" />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{order.technicianName && (
|
||||
<div className="detail-item">
|
||||
<span className="label">处理人</span>
|
||||
@@ -265,6 +293,33 @@ function AftersalesConfirmPage() {
|
||||
|
||||
{isPending && (
|
||||
<>
|
||||
<div className="aftersales-upload-section">
|
||||
<p className="aftersales-signature-section-title">现场图片</p>
|
||||
<label className="aftersales-upload-trigger">
|
||||
<UploadOutlined />
|
||||
<span>{uploadingImages ? '上传中...' : '上传现场图片'}</span>
|
||||
<small>最多 6 张,单张不超过 5MB</small>
|
||||
<input
|
||||
type="file"
|
||||
accept="image/jpeg,image/png,image/webp,image/heic,image/heif"
|
||||
multiple
|
||||
disabled={uploadingImages}
|
||||
onChange={(e) => {
|
||||
handleUploadSiteImages(e.target.files);
|
||||
e.currentTarget.value = '';
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
{order.siteImages && order.siteImages.length > 0 && (
|
||||
<div className="aftersales-site-images">
|
||||
{order.siteImages.map((url) => (
|
||||
<a key={url} href={url} target="_blank" rel="noreferrer">
|
||||
<img src={url} alt="现场图片" />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="aftersales-signature-section">
|
||||
<p className="aftersales-signature-section-title">请签名确认维修结果</p>
|
||||
<div className="aftersales-signature-grid">
|
||||
|
||||
@@ -251,6 +251,10 @@ function AftersalesDetailPage() {
|
||||
.electronic-form-signature-title { flex: 0 0 auto; margin: 0 10px 0 0; font-weight: 600; white-space: nowrap; }
|
||||
.electronic-form-signature-stage { flex: 1; height: 72px; min-width: 160px; display: flex; align-items: center; justify-content: center; border-bottom: 1px solid #1f2937; }
|
||||
.electronic-form-signature-img { max-width: 180px; max-height: 68px; object-fit: contain; }
|
||||
.electronic-form-site-images { margin-top: 18px; }
|
||||
.electronic-form-site-images-title { margin: 0 0 10px; font-weight: 600; }
|
||||
.electronic-form-site-images-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
|
||||
.electronic-form-site-images-grid img { width: 100%; height: 150px; object-fit: cover; border: 1px solid #1f2937; }
|
||||
</style>
|
||||
</head>
|
||||
<body>${formNode.outerHTML}</body>
|
||||
@@ -618,6 +622,16 @@ function AftersalesDetailPage() {
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{order.siteImages && order.siteImages.length > 0 && (
|
||||
<div className="electronic-form-site-images">
|
||||
<p className="electronic-form-site-images-title">现场图片</p>
|
||||
<div className="electronic-form-site-images-grid">
|
||||
{order.siteImages.map((url) => (
|
||||
<img key={url} src={url} alt="现场图片" />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="electronic-form-signatures">
|
||||
<div className="electronic-form-signature-box">
|
||||
<p className="electronic-form-signature-title">处理人签名:</p>
|
||||
|
||||
@@ -37,6 +37,12 @@
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.aftersales-upload-section {
|
||||
margin-top: 24px;
|
||||
padding-top: 24px;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.aftersales-signature-section-title {
|
||||
margin: 0 0 14px;
|
||||
color: #111827;
|
||||
@@ -44,6 +50,58 @@
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.aftersales-upload-trigger {
|
||||
min-height: 116px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
border: 1.5px dashed #94a3b8;
|
||||
border-radius: 12px;
|
||||
background: #f8fafc;
|
||||
color: #1f2937;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.aftersales-upload-trigger input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.aftersales-upload-trigger .anticon {
|
||||
color: #1677ff;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.aftersales-upload-trigger small {
|
||||
color: #6b7280;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.aftersales-site-images {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.aftersales-site-images a {
|
||||
display: block;
|
||||
aspect-ratio: 1;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.aftersales-site-images img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.aftersales-signature-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
|
||||
@@ -115,6 +115,28 @@
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.electronic-form-site-images {
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.electronic-form-site-images-title {
|
||||
margin: 0 0 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.electronic-form-site-images-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.electronic-form-site-images-grid img {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
object-fit: cover;
|
||||
border: 1px solid #1f2937;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.electronic-form-header {
|
||||
align-items: flex-start;
|
||||
|
||||
Reference in New Issue
Block a user