设备接入产品配置 界面

This commit is contained in:
mmc
2026-05-25 16:14:09 +08:00
parent 59f6b1e763
commit 2471479b9c
3 changed files with 118 additions and 64 deletions

View File

@@ -213,12 +213,25 @@ export default function DeviceAccessPage() {
if (type === 'product') {
setProductProtocol(record?.protocolCode || '');
const modelId = record.thingsModels ? Object.keys(record.thingsModels)[0] : null;
const limitConfig = modelId ? record.thingsModels[modelId] : {};
// 👇 👇 👇 这里是关键:回显 limitConfig + actionConfig
let limitConfig = {};
let actionConfig = {};
if (modelId && record.thingsModels[modelId]) {
limitConfig = record.thingsModels[modelId].properties || {};
actionConfig = record.thingsModels[modelId].actions || {}; // 👈 行为配置回显
}
if (modelId) {
const targetModel = thinsModelList.find(m => m.id == modelId);
setSelectedModelId(Number(modelId));
setPropertyList(targetModel?.supportProperties || []);
form.setFieldsValue({ ...record, modelId, limitConfig });
form.setFieldsValue({
...record,
modelId,
limitConfig,
actionConfig, // 👈 回显
});
} else {
form.setFieldsValue(record);
setSelectedModelId('');
@@ -319,14 +332,23 @@ export default function DeviceAccessPage() {
if (modalType === 'product') {
form.setFieldValue('modelId', selectedModelId);
const { modelId, limitConfig, ...params } = values;
const { modelId, limitConfig, actionConfig, ...params } = values;
console.log('提交的产品数据:', { ...params, modelId, limitConfig, actionConfig }); // 👈 这里可以看到提交的数据结构
const finalLimitConfig = {};
if (limitConfig) {
Object.keys(limitConfig).forEach(key => {
finalLimitConfig[key] = { min: limitConfig[key].min ?? 0, max: limitConfig[key].max ?? 100 };
});
}
const thingsModels = modelId ? { [modelId]: finalLimitConfig } : {};
const thingsModels = modelId ? {
[modelId]: {
properties: finalLimitConfig,
actions: actionConfig || {},
}
} : {};
const submitData = { ...params, thingsModels, id: editData?.id };
const res = editData ? await updateProduct(submitData) : await addProduct(submitData);
if (res.code === 200) {
@@ -521,7 +543,7 @@ export default function DeviceAccessPage() {
<Option value="udp">UDP</Option>
</Select>
</Form.Item>
<Form.Item name="status" label="status" initialValue={1}>
<Form.Item name="status" label="状态" initialValue={1}>
<Select placeholder="状态" allowClear style={{ width: 100 }}>
<Option value={1}>启用</Option>
<Option value={0}>禁用</Option>
@@ -763,6 +785,9 @@ export default function DeviceAccessPage() {
<Form.Item label="厂商" name="manufacturer">
<Input placeholder="迈步科技" />
</Form.Item>
<Form.Item name="actionConfig" hidden>
<Input />
</Form.Item>
<Form.Item label="传输类型" name="transport">
<Select placeholder="传输方式">
@@ -780,7 +805,7 @@ export default function DeviceAccessPage() {
</div>
<Divider>绑定物模型</Divider>
<Form.Item label="绑定物模型">
<Form.Item label="绑定物模型" name="modelId">
<Select
placeholder="请选择要绑定的物模型"
style={{ width: '100%' }}
@@ -792,11 +817,23 @@ export default function DeviceAccessPage() {
setPropertyList(model.supportProperties || []);
const protocol = model.connectType || '';
form.setFieldsValue({ protocolCode: protocol });
// 属性默认配置
const defaultLimit = {};
model.supportProperties?.forEach(attr => {
defaultLimit[attr.propertyKey] = { min: 0, max: 100 };
});
form.setFieldsValue({ limitConfig: defaultLimit });
// 行为默认配置
const actionLimit = {};
model.supportActions?.forEach(action => {
actionLimit[action.actionId] = action.params || {};
});
form.setFieldsValue({
limitConfig: defaultLimit,
actionConfig: actionLimit,
});
} else {
setSelectedModelId('');
setPropertyList([]);
@@ -815,6 +852,9 @@ export default function DeviceAccessPage() {
<Form.Item name="protocolCode" hidden>
<Input />
</Form.Item>
<Form.Item name="actionConfig" hidden>
<Input />
</Form.Item>
{selectedModelId && (
<>
@@ -832,61 +872,76 @@ export default function DeviceAccessPage() {
</>
)}
{form.getFieldValue('protocolCode')?.toLowerCase() === 'onvif' && (
<>
<Divider>协议自动配置项</Divider>
<div className="grid grid-cols-2 gap-4">
<Form.Item label="IP地址" name="ip">
<Input placeholder="192.168.1.65" />
</Form.Item>
<Form.Item label="端口" name="port">
<InputNumber className="w-full" defaultValue={80} />
</Form.Item>
<Form.Item label="账号" name="username">
<Input placeholder="admin" />
</Form.Item>
<Form.Item label="密码" name="password">
<Input.Password placeholder="密码" />
</Form.Item>
{/* ==== 属性配置(你原来的) ==== */}
<Divider>物模型属性配置</Divider>
<div style={{ marginTop: 12 }}>
{propertyList?.map((item) => (
<div key={item.id} style={{ padding: '12px 16px', border: '1px solid #eee', borderRadius: 8, marginBottom: 8 }}>
<div style={{ marginBottom: 8, fontWeight: 500 }}>
{item.propertyName}({item.propertyKey})
</div>
<div className="flex gap-3">
<Form.Item label="最小值" name={['limitConfig', item.propertyKey, 'min']} style={{ flex: 1 }}>
<InputNumber style={{ width: '100%' }} placeholder="最小值" />
</Form.Item>
<Form.Item label="最大值" name={['limitConfig', item.propertyKey, 'max']} style={{ flex: 1 }}>
<InputNumber style={{ width: '100%' }} placeholder="最大值" />
</Form.Item>
</div>
</div>
</>
)}
))}
{(!propertyList || propertyList.length === 0) && (
<div style={{ color: '#999', textAlign: 'center', padding: 10 }}>
请选择物模型以显示属性
</div>
)}
</div>
{form.getFieldValue('protocolCode')?.toLowerCase() === 'rtsp' && (
<>
<Divider>协议自动配置项</Divider>
<Form.Item label="RTSP地址" name="rtspUrl">
<Input placeholder="rtsp://username:password@ip:port/stream" />
</Form.Item>
</>
)}
{/* ==== ✅ 行为(指令)配置 新增 ==== */}
<Divider>物模型行为(指令)配置</Divider>
<div style={{ marginTop: 12 }}>
{thinsModelList.find(m => m.id === selectedModelId)?.supportActions?.map((action) => (
<div key={action.actionId} style={{ padding: '12px 16px', border: '1px solid #eee', borderRadius: 8, marginBottom: 8 }}>
<div style={{ marginBottom: 8, fontWeight: 500 }}>
{action.actionName}({action.actionId})
</div>
{Object.entries(action.params || {}).map(([paramKey, paramCfg]: [string, any]) => (
<div key={paramKey} className="flex gap-3 mt-2">
<Form.Item
label={`${paramKey} 默认值`}
name={['actionConfig', action.actionId, paramKey, 'default']}
style={{ flex: 1 }}
>
<Input style={{ width: '100%' }} placeholder="默认值" />
</Form.Item>
<Form.Item
label="类型"
name={['actionConfig', action.actionId, paramKey, 'type']}
style={{ flex: 1 }}
>
<Input disabled style={{ width: '100%' }} placeholder="类型" />
</Form.Item>
<Form.Item
label="描述"
name={['actionConfig', action.actionId, paramKey, 'description']}
style={{ flex: 2 }}
>
<Input disabled style={{ width: '100%' }} placeholder="描述" />
</Form.Item>
</div>
))}
</div>
))}
{(!thinsModelList.find(m => m.id === selectedModelId)?.supportActions?.length) && (
<div style={{ color: '#999', textAlign: 'center', padding: 10 }}>
该物模型暂无指令
</div>
)}
</div>
</>
)}
<Divider>物模型属性配置</Divider>
<Form.Item name="modelId" hidden><Input /></Form.Item>
<div style={{ marginTop: 12 }}>
{propertyList?.map((item) => (
<div key={item.id} style={{ padding: '12px 16px', border: '1px solid #eee', borderRadius: 8, marginBottom: 8 }}>
<div style={{ marginBottom: 8, fontWeight: 500 }}>
{item.propertyName}({item.propertyKey})
</div>
<div className="flex gap-3">
<Form.Item label="最小值" name={['limitConfig', item.propertyKey, 'min']} style={{ flex: 1 }}>
<InputNumber style={{ width: '100%' }} placeholder="最小值" />
</Form.Item>
<Form.Item label="最大值" name={['limitConfig', item.propertyKey, 'max']} style={{ flex: 1 }}>
<InputNumber style={{ width: '100%' }} placeholder="最大值" />
</Form.Item>
</div>
</div>
))}
{(!propertyList || propertyList.length === 0) && (
<div style={{ color: '#999', textAlign: 'center', padding: 10 }}>
请选择物模型以显示属性
</div>
)}
</div>
</>
)}
{modalType === 'model' && (

View File

@@ -112,7 +112,7 @@ export default function DeviceOverviewPage({ devices, planedevices, devicesAll,
};
return (
<Content style={{ maxWidth: 1800, margin: '0 auto', }}>
<Content style={{ maxWidth: 1800, margin: '0 8', }}>
<Row gutter={[8, 8]} style={{ marginBottom: 12, flexWrap: 'wrap' }}>
{dynamicTopCards.map((card, idx) => (
<Col key={idx} style={{ minWidth: '160px', flex: '1 0 auto' }}>

View File

@@ -421,7 +421,7 @@ export default function WayLinePage({ planedevices }) {
};
return (
<div className="normal-spacing" style={{ background: '#f5f7fa', minHeight: '100vh', padding: '16px', paddingTop: 0 }}>
<div className="normal-spacing" style={{ background: '#f5f7fa', minHeight: '100vh', padding: '0px', paddingTop: 0 }}>
<Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
{topCards.map((item, index) => (
<Col xs={12} sm={8} md={4} key={index}>
@@ -527,8 +527,8 @@ export default function WayLinePage({ planedevices }) {
</Tooltip>
}
bordered={false}
bodyStyle={{ padding: 0, display: 'flex', flexDirection: 'column', flex: 1 }}
style={{ ...cardStyle, flex: 1 }}
bodyStyle={{ padding: 0, display: 'flex', flexDirection: 'column', height: "calc(100% - 40px)", overflow: "auto" }}
style={{ ...cardStyle, height: "410px" }}
>
<Table
size="small"
@@ -536,8 +536,7 @@ export default function WayLinePage({ planedevices }) {
columns={taskColumns}
dataSource={taskList}
pagination={false}
scroll={{ y: 'auto', maxHeight: 260 }}
style={{ height: '100%' }}
style={{ height: "calc(100% - 40px)" }}
rowClassName={(record) =>
selectedTask?.uuid === record.uuid ? 'table-row-highlight' : ''
}