227 lines
6.5 KiB
TypeScript
227 lines
6.5 KiB
TypeScript
/**
|
||
* 柜子详情页
|
||
* - 显示柜子基本信息(ID、状态)
|
||
* - 按仓控板分组展示 6 个通道的状态卡片
|
||
*/
|
||
import { useEffect, useState } from 'react';
|
||
import { useParams, useNavigate } from 'react-router-dom';
|
||
import {
|
||
Card,
|
||
Tag,
|
||
Typography,
|
||
Spin,
|
||
Grid,
|
||
Button,
|
||
Descriptions,
|
||
Message,
|
||
Modal,
|
||
} from '@arco-design/web-react';
|
||
import { IconLeft, IconDesktop } from '@arco-design/web-react/icon';
|
||
import { cabinetApi, type CabinetDetail } from '@/api/devices';
|
||
|
||
const Row = Grid.Row;
|
||
const Col = Grid.Col;
|
||
|
||
/** 状态映射 */
|
||
const STATUS_MAP: Record<number, { label: string; color: string }> = {
|
||
0: { label: '空闲', color: 'green' },
|
||
1: { label: '充电中', color: 'blue' },
|
||
2: { label: '故障', color: 'red' },
|
||
3: { label: '禁用', color: 'orange' },
|
||
};
|
||
|
||
/** 柜子状态映射 */
|
||
const CABINET_STATUS_MAP: Record<number, { label: string; color: string }> = {
|
||
0: { label: '离线', color: 'gray' },
|
||
1: { label: '在线', color: 'green' },
|
||
2: { label: '充电中', color: 'blue' },
|
||
3: { label: '故障', color: 'red' },
|
||
};
|
||
|
||
export default function CabinetDetail() {
|
||
const { id } = useParams<{ id: string }>();
|
||
const navigate = useNavigate();
|
||
const [loading, setLoading] = useState(true);
|
||
const [detail, setDetail] = useState<CabinetDetail | null>(null);
|
||
const [powLoading, setPowLoading] = useState(false);
|
||
|
||
useEffect(() => {
|
||
if (!id) return;
|
||
setLoading(true);
|
||
cabinetApi
|
||
.getDetail(Number(id))
|
||
.then((res) => setDetail(res.data))
|
||
.catch(() => {
|
||
// 如果详情接口不可用,显示提示
|
||
})
|
||
.finally(() => setLoading(false));
|
||
}, [id]);
|
||
|
||
/** 接触器合闸 */
|
||
const handlePowOn = () => {
|
||
if (!id) return;
|
||
Modal.confirm({
|
||
title: '确认操作',
|
||
content: '确定要合闸恢复380V供电吗?',
|
||
onOk: async () => {
|
||
setPowLoading(true);
|
||
try {
|
||
await cabinetApi.powOn(Number(id));
|
||
Message.success('合闸指令已发送');
|
||
// 刷新详情
|
||
const res = await cabinetApi.getDetail(Number(id));
|
||
setDetail(res.data);
|
||
} catch {
|
||
Message.error('操作失败');
|
||
} finally {
|
||
setPowLoading(false);
|
||
}
|
||
},
|
||
});
|
||
};
|
||
|
||
/** 接触器分闸 */
|
||
const handlePowOff = () => {
|
||
if (!id) return;
|
||
Modal.confirm({
|
||
title: '确认操作',
|
||
content: '确定要分闸切断380V供电吗?',
|
||
onOk: async () => {
|
||
setPowLoading(true);
|
||
try {
|
||
await cabinetApi.powOff(Number(id));
|
||
Message.success('分闸指令已发送');
|
||
// 刷新详情
|
||
const res = await cabinetApi.getDetail(Number(id));
|
||
setDetail(res.data);
|
||
} catch {
|
||
Message.error('操作失败');
|
||
} finally {
|
||
setPowLoading(false);
|
||
}
|
||
},
|
||
});
|
||
};
|
||
|
||
if (loading) {
|
||
return (
|
||
<Card>
|
||
<div style={{ textAlign: 'center', padding: '60px 0' }}>
|
||
<Spin dot />
|
||
</div>
|
||
</Card>
|
||
);
|
||
}
|
||
|
||
if (!detail) {
|
||
return (
|
||
<Card>
|
||
<Typography.Text type="secondary">柜子不存在或加载失败</Typography.Text>
|
||
</Card>
|
||
);
|
||
}
|
||
|
||
const cabStatus = CABINET_STATUS_MAP[detail.status] || { label: '未知', color: 'gray' };
|
||
|
||
return (
|
||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||
{/* 顶部导航 */}
|
||
<Card>
|
||
<div
|
||
style={{
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
}}
|
||
>
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||
<Button
|
||
type="text"
|
||
icon={<IconLeft />}
|
||
onClick={() => navigate('/devices')}
|
||
>
|
||
返回
|
||
</Button>
|
||
<IconDesktop style={{ fontSize: 24, color: 'var(--color-text-2)' }} />
|
||
<Typography.Title heading={5} style={{ margin: 0 }}>
|
||
柜子 {detail.abstract_id}
|
||
</Typography.Title>
|
||
<Tag color={cabStatus.color}>{cabStatus.label}</Tag>
|
||
</div>
|
||
</div>
|
||
</Card>
|
||
|
||
{/* 柜子信息 */}
|
||
<Card title="基本信息" size="small">
|
||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
|
||
<Descriptions
|
||
data={[
|
||
{ label: '抽象 ID', value: detail.abstract_id },
|
||
{ label: 'IMEI', value: detail.imei },
|
||
{ label: '名称', value: detail.name || '-' },
|
||
{ label: '状态', value: cabStatus.label },
|
||
]}
|
||
column={2}
|
||
layout="horizontal"
|
||
labelStyle={{ paddingRight: 36 }}
|
||
/>
|
||
<div style={{ display: 'flex', gap: 8 }}>
|
||
<Button
|
||
type="primary"
|
||
status="success"
|
||
loading={powLoading}
|
||
onClick={handlePowOn}
|
||
>
|
||
合闸供电
|
||
</Button>
|
||
<Button
|
||
type="primary"
|
||
status="danger"
|
||
loading={powLoading}
|
||
onClick={handlePowOff}
|
||
>
|
||
分闸断电
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</Card>
|
||
|
||
{/* 仓控板列表 */}
|
||
{detail.cabin_boards.map((board) => (
|
||
<Card
|
||
key={board.id}
|
||
title={`仓板 ${board.board_id} (ID: ${board.id})`}
|
||
size="small"
|
||
>
|
||
<Row gutter={[16, 16]}>
|
||
{board.compartments.map((comp) => {
|
||
const st = STATUS_MAP[comp.status] || { label: '未知', color: 'gray' };
|
||
return (
|
||
<Col key={comp.id} xs={12} sm={8} md={6} lg={4}>
|
||
<Card
|
||
size="small"
|
||
hoverable
|
||
style={{ textAlign: 'center' }}
|
||
>
|
||
<div style={{ fontSize: 16, fontWeight: 600, marginBottom: 8 }}>
|
||
通道 {comp.channel_id}
|
||
</div>
|
||
<Tag color={st.color} style={{ marginBottom: 8 }}>
|
||
{st.label}
|
||
</Tag>
|
||
<div style={{ fontSize: 12, color: 'var(--color-text-3)', lineHeight: 1.8 }}>
|
||
<div>电压: 0.0V</div>
|
||
<div>电流: 0.0A</div>
|
||
<div>功率: 0W</div>
|
||
</div>
|
||
</Card>
|
||
</Col>
|
||
);
|
||
})}
|
||
</Row>
|
||
</Card>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|