227 lines
6.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 柜子详情页
* - 显示柜子基本信息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>
);
}