无人机 绑定解绑

This commit is contained in:
mmc
2026-05-25 14:39:05 +08:00
parent 44e3013836
commit 8e422af07b
2 changed files with 63 additions and 25 deletions

View File

@@ -16,6 +16,7 @@ import {
Tooltip,
Row,
Col,
App,
} from 'antd';
import {
SearchOutlined,
@@ -28,7 +29,7 @@ import {
} from '@ant-design/icons';
import SerialNumGenerate from "./SerialNumGenerate"
import { getPlaneDeviceList, bindDrone, unbindDrone } from '@/api/device';
import { getOrgList } from '@/api/organization/index.tsx';
import { getOrgList } from '@/api/organization/index';
import { getSiteByOrgId } from '@/api/stationManage/index';
import { getUser } from '@/api/user/index';
import { useSelector } from 'react-redux';
@@ -63,6 +64,8 @@ const tabs = [
export default function DeviceManagementPage() {
const [activeTab, setActiveTab] = useState('all');
const { modal } = App.useApp();
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const [deviceList, setDeviceList] = useState(allDeviceList);
@@ -154,11 +157,11 @@ export default function DeviceManagementPage() {
const payload = {
siteId: values.siteId,
orgId: values.orgId,
userId: values.userId || userInfo?.userId,
userId: values.userId,
sns: values.sns.split(',').map(s => s.trim()),
};
const res: any = await bindDrone(payload);
if (res.code === 200) {
if (res.code === 200 && res.data == true) {
utils.message.success('绑定成功');
setBindModalVisible(false);
bindForm.resetFields();
@@ -172,13 +175,31 @@ export default function DeviceManagementPage() {
};
const handleUnbind = (sns, type) => {
// ✅ 关键:过滤掉空、null、undefined、纯空格的项
const validSns = (sns || []).filter(item =>
item !== null &&
item !== undefined &&
item !== '' &&
item.trim() !== ''
);
// 如果过滤完没有有效SN,直接提示
if (validSns.length === 0) {
return utils.message.warning('未选择有效设备,无法解绑');
}
const typeText = { 1: '人员', 2: '场站', 3: '组织' }[type];
Modal.confirm({
modal.confirm({
title: '确认解绑',
content: `确定要为选中的无人机清除${typeText}绑定吗?`,
okText: '确定',
cancelText: '取消',
onOk: async () => {
try {
const res: any = await unbindDrone({ sns, type });
// ✅ 传给接口的是过滤后的有效数组
const res: any = await unbindDrone({ sns: validSns, type });
if (res.code === 200) {
utils.message.success('解绑成功');
fetchDroneList();
@@ -201,16 +222,17 @@ export default function DeviceManagementPage() {
{
title: '操作',
key: 'action',
fixed: 'right',
render: (_, record) => (
<Space>
<Tooltip title="清除人员绑定">
<Button size="small" icon={<DisconnectOutlined />} onClick={() => handleUnbind([record.device_sn], 1)}>人</Button>
<Button size="small" icon={<DisconnectOutlined />} onClick={() => handleUnbind([record.gateway_sn], 1)}>用户</Button>
</Tooltip>
<Tooltip title="清除场站绑定">
<Button size="small" icon={<DisconnectOutlined />} onClick={() => handleUnbind([record.device_sn], 2)}>场</Button>
<Button size="small" icon={<DisconnectOutlined />} onClick={() => handleUnbind([record.gateway_sn], 2)}>场站</Button>
</Tooltip>
<Tooltip title="清除组织绑定">
<Button size="small" icon={<DisconnectOutlined />} onClick={() => handleUnbind([record.device_sn], 3)}>组</Button>
<Button size="small" icon={<DisconnectOutlined />} onClick={() => handleUnbind([record.gateway_sn], 3)}>组织</Button>
</Tooltip>
</Space>
),
@@ -292,7 +314,7 @@ export default function DeviceManagementPage() {
// 删除
const handleDelete = (record) => {
Modal.confirm({
modal.confirm({
title: '确认删除',
content: `确定要删除设备 ${record.name} 吗?`,
onOk: () => {
@@ -310,9 +332,10 @@ export default function DeviceManagementPage() {
style={{ marginBottom: 0 }}
>
<Tabs.TabPane key="all" tab="设备管理" />
<Tabs.TabPane key="drone" tab="无人机管理" />
<Tabs.TabPane key="camera" tab="摄像头管理" />
<Tabs.TabPane key="smart" tab="智能装备" />
<Tabs.TabPane key="drone" tab="无人机管理" />
<Tabs.TabPane key="serialGenerate" tab="机器人生成" />
</Tabs>
@@ -330,17 +353,30 @@ export default function DeviceManagementPage() {
</Space>
</div>
<Table
rowSelection={{
selectedRowKeys: selectedDroneKeys,
onChange: setSelectedDroneKeys,
}}
rowKey="device_sn"
columns={droneColumns}
dataSource={droneList}
loading={loading}
pagination={{ showSizeChanger: true, showTotal: t => `共 ${t} 条` }}
/>
<div style={{
height: 'calc(100vh - 220px)',
overflowY: 'auto',
paddingRight: '4px',
}}>
<Table
rowSelection={{
selectedRowKeys: selectedDroneKeys,
onChange: setSelectedDroneKeys,
}}
rowKey="gateway_sn"
columns={droneColumns}
dataSource={droneList}
loading={loading}
pagination={{
showSizeChanger: true,
showTotal: t => `共 ${t} 条`
}}
scroll={{
y: 'auto ', // 最大高度,超过才滚动
x: '700px' /* 横向最小宽度,不够自动出滚动条 */
}}
/>
</div>
<Modal
title="绑定无人机"
@@ -383,7 +419,7 @@ export default function DeviceManagementPage() {
</Form.Item>
{/* ========== 修复:场站 ========== */}
<Form.Item label="所属场站" name="siteId" rules={[{ required: true, message: '请选择场站' }]}>
<Form.Item label="所属场站" name="siteId" >
<Select
placeholder="请先选择组织"
disabled={!siteOptions.length}
@@ -405,7 +441,7 @@ export default function DeviceManagementPage() {
</Form.Item>
{/* ========== 修复:用户 ========== */}
<Form.Item label="负责人" name="userId" rules={[{ required: true, message: '请选择负责人' }]}>
<Form.Item label="负责人" name="userId" >
<Select
placeholder="请先选择场站"
disabled={!userOptions.length}

View File

@@ -224,8 +224,10 @@ const DroneVideoPlayer: React.FC<DroneVideoPlayerProps> = ({
flexDirection: 'column',
gap: 8
}}>
{loading ? <ReloadOutlined spin /> : null}
<Text style={{ color: '#ccc' }}>{loading ? '视频加载中...' : '暂无视频信号'}</Text>
{loading ? <ReloadOutlined spin style={{ color: '#fff' }} /> : null}
<span style={{ color: '#fff', fontSize: 12 }}>
{loading ? '视频加载中...' : '暂无视频信号'}
</span>
</div>
)}