168 lines
5.6 KiB
TypeScript
168 lines
5.6 KiB
TypeScript
'use client';
|
||
|
||
import React, { useEffect, useState } from 'react';
|
||
import { Alert, Card, Row, Col, Typography, Space, Button, Tabs, Table, Image as AntImage, Empty, Spin, Flex } from 'antd';
|
||
import { ArrowLeftOutlined } from '@ant-design/icons';
|
||
import { useRouter } from 'next/navigation';
|
||
import { Breadcrumb } from '../../../components/Breadcrumb';
|
||
import { MockApi } from '../../../services/mockApi';
|
||
import { MachineDetail, ImageItem } from '../../../types';
|
||
import { StatusBadge } from '../../../components/StatusBadge';
|
||
|
||
const { Text } = Typography;
|
||
|
||
export default function MachineDetailPage({ params }: { params: { serialNumber: string } }) {
|
||
const router = useRouter();
|
||
const [machine, setMachine] = useState<MachineDetail | null>(null);
|
||
const [loading, setLoading] = useState(true);
|
||
const [errorMessage, setErrorMessage] = useState('');
|
||
|
||
useEffect(() => {
|
||
let isMounted = true;
|
||
|
||
const loadMachineDetail = async () => {
|
||
try {
|
||
setLoading(true);
|
||
setErrorMessage('');
|
||
const data = await MockApi.getMachineDetail(params.serialNumber);
|
||
if (!isMounted) return;
|
||
setMachine(data);
|
||
} catch {
|
||
if (!isMounted) return;
|
||
setMachine(null);
|
||
setErrorMessage('机器详情加载失败,请稍后重试');
|
||
} finally {
|
||
if (isMounted) {
|
||
setLoading(false);
|
||
}
|
||
}
|
||
};
|
||
|
||
loadMachineDetail();
|
||
|
||
return () => {
|
||
isMounted = false;
|
||
};
|
||
}, [params.serialNumber]);
|
||
|
||
if (loading) {
|
||
return (
|
||
<Flex vertical align="center" justify="center" style={{ padding: 48 }}>
|
||
<Spin tip="正在加载机器详情..." />
|
||
</Flex>
|
||
);
|
||
}
|
||
|
||
if (!machine) {
|
||
return (
|
||
<Flex vertical align="center" style={{ padding: 48 }}>
|
||
{errorMessage && (
|
||
<Alert
|
||
type="error"
|
||
message={errorMessage}
|
||
showIcon
|
||
style={{ maxWidth: 480, marginBottom: 16 }}
|
||
/>
|
||
)}
|
||
<Empty description="暂无机器详情" />
|
||
<Button type="primary" onClick={() => router.push('/machines')} style={{ marginTop: 16 }}>
|
||
返回机器查询
|
||
</Button>
|
||
</Flex>
|
||
);
|
||
}
|
||
|
||
const renderImageGroup = (images: ImageItem[]) => {
|
||
if (!images || images.length === 0) return <Empty description="暂无图片" />;
|
||
|
||
return (
|
||
<AntImage.PreviewGroup>
|
||
<Space size={[16, 16]} wrap>
|
||
{images.map((img) => (
|
||
<Flex
|
||
key={img.id}
|
||
vertical
|
||
style={{
|
||
position: 'relative',
|
||
width: 120,
|
||
gap: 4
|
||
}}
|
||
>
|
||
<div style={{ width: '100%', aspectRatio: '4/3', overflow: 'hidden', borderRadius: 8, background: '#f0f0f0' }}>
|
||
<AntImage
|
||
src={img.url}
|
||
alt={img.name}
|
||
width="100%"
|
||
height="100%"
|
||
style={{ objectFit: 'cover' }}
|
||
preview={{
|
||
src: img.url,
|
||
}}
|
||
/>
|
||
</div>
|
||
<Text style={{ fontSize: 12, textAlign: 'center' }}>{img.name}</Text>
|
||
<Text type="secondary" style={{ fontSize: 11, textAlign: 'center' }}>{img.createdAt}</Text>
|
||
</Flex>
|
||
))}
|
||
</Space>
|
||
</AntImage.PreviewGroup>
|
||
);
|
||
};
|
||
|
||
const imageTabs = [
|
||
{ key: 'incoming', label: '来料检验单', children: renderImageGroup(machine.images.incomingInspection) },
|
||
{ key: 'startup', label: '开机测试样张', children: renderImageGroup(machine.images.startupTestSample) },
|
||
{ key: 'production', label: '生产加工单', children: renderImageGroup(machine.images.productionOrder) },
|
||
{ key: 'robot', label: '机器人查验拍照', children: renderImageGroup(machine.images.robotInspection) },
|
||
];
|
||
|
||
return (
|
||
<div>
|
||
<Flex justify="space-between" align="center" style={{ marginBottom: 16 }}>
|
||
<Breadcrumb />
|
||
<Button icon={<ArrowLeftOutlined />} onClick={() => router.back()}>返回查询</Button>
|
||
</Flex>
|
||
|
||
<Card title="机器基本信息" style={{ marginBottom: 24 }}>
|
||
<Row gutter={[24, 16]}>
|
||
<Col span={8}>
|
||
<Text type="secondary">序列号:</Text> <Text strong>{machine.serialNumber}</Text>
|
||
</Col>
|
||
<Col span={8}>
|
||
<Text type="secondary">机器型号:</Text> <Text strong>{machine.modelName}</Text>
|
||
</Col>
|
||
<Col span={8}>
|
||
<Text type="secondary">所属报关单:</Text> <Button type="link">{machine.customsId}</Button>
|
||
</Col>
|
||
<Col span={8}>
|
||
<Text type="secondary">当前状态:</Text> <StatusBadge status={machine.status} />
|
||
</Col>
|
||
{Object.entries(machine.specs).map(([key, value]) => (
|
||
<Col span={8} key={key}>
|
||
<Text type="secondary">{key}:</Text> <Text>{value}</Text>
|
||
</Col>
|
||
))}
|
||
</Row>
|
||
</Card>
|
||
|
||
<Card title="图片资料" style={{ marginBottom: 24 }}>
|
||
<Tabs items={imageTabs} />
|
||
</Card>
|
||
|
||
<Card title="查验记录">
|
||
<Table
|
||
dataSource={machine.inspectionRecords}
|
||
rowKey="id"
|
||
pagination={false}
|
||
locale={{ emptyText: <Empty description="暂无查验记录" /> }}
|
||
>
|
||
<Table.Column title="查验时间" dataIndex="time" />
|
||
<Table.Column title="操作人" dataIndex="operator" />
|
||
<Table.Column title="结果" dataIndex="result" />
|
||
<Table.Column title="备注" dataIndex="remark" />
|
||
</Table>
|
||
</Card>
|
||
</div>
|
||
);
|
||
}
|