Browse Source

feat: 完善中值滤波算法配置组件,添加输入验证和提示信息

master
cles 1 month ago
parent
commit
126189598b
  1. 193
      src/renderer/src/components/AlgorithmSettings/AlgorithmSettings.jsx

193
src/renderer/src/components/AlgorithmSettings/AlgorithmSettings.jsx

@ -4,13 +4,59 @@ import { useState, useEffect } from 'react'
import useDeviceStore from '../../stores/deviceStore' import useDeviceStore from '../../stores/deviceStore'
import { IPC_EVENT } from '../../common/ipcEvents' import { IPC_EVENT } from '../../common/ipcEvents'
/**
* 中值滤波算法配置组件
*
* @description
* 该组件用于配置设备的中值滤波算法参数支持对每个测点进行独立配置
*
* @业务规则
* 1. 窗口大小xLen/yLen必须满足以下条件之一
* - 输入 0 1表示该方向不启用中值滤波
* - 输入 >= 3 的奇数 3, 5, 7, 9...表示启用中值滤波数值越大滤波效果越强
* - 取值范围0-50
*
* 2. 数据加载时机
* - 只在 Modal 打开时才从设备加载配置数据按需加载
* - 避免组件挂载时的不必要请求
*
* 3. 输入验证时机
* - onChange允许自由输入不进行验证支持多位数输入如 2131
* - onBlur失去焦点时才进行验证不合法的值会被清空并提示
*
* @props
* @param {boolean} visible - Modal 是否可见
* @param {function} onClose - 关闭 Modal 的回调函数
*
* @example
* <AlgorithmSettings
* visible={modalVisible}
* onClose={() => setModalVisible(false)}
* />
*/
function AlgorithmSettings({ visible, onClose }) { function AlgorithmSettings({ visible, onClose }) {
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const connectedDevice = useDeviceStore((state) => state.connectedDevice) const connectedDevice = useDeviceStore((state) => state.connectedDevice)
console.log('当前连接的设备:', connectedDevice) console.log('当前连接的设备:', connectedDevice)
// //
const [pointSettings, setPointSettings] = useState([]) // { id, pos, xLen, yLen, enable }
const [pointSettings, setPointSettings] = useState([
{
id: 1,
pos: '点位1',
xLen: null,
yLen: null,
enable: false
},{
id: 2,
pos: '点位2',
xLen: null,
yLen: null,
enable: false
}
])
// Modal // Modal
useEffect(() => { useEffect(() => {
@ -63,14 +109,61 @@ function AlgorithmSettings({ visible, onClose }) {
loadMedianFilterConfig() loadMedianFilterConfig()
}, [visible, connectedDevice]) // visible Modal }, [visible, connectedDevice]) // visible Modal
// /**
* 验证窗口大小输入是否合法
*
* @param {number|null|string} value - 要验证的值
* @returns {boolean} - 是否通过验证
*
* @规则
* - 允许空值null ''
* - 允许 0 1表示不启用该方向的中值滤波
* - 允许大于等于 3 的奇数3, 5, 7, 9, 11...
* - 取值范围0-50
* - 不允许负数偶数2, 4, 6...除了0
*/
const validateWindowSize = (value) => {
//
if (value === null || value === '') return true
const num = Number(value)
//
if (num < 0 || num > 50) return false
// 01
if (num === 0 || num === 1) return true
// 3
if (num >= 3 && num % 2 === 1) return true
return false
}
/**
* 更新点位配置
* 主要用于 Switch 组件的 onChange 事件
*
* @param {number} id - 点位 ID
* @param {string} field - 要更新的字段名'enable', 'xLen', 'yLen'
* @param {any} value - 新值
*/
const updatePointSetting = (id, field, value) => { const updatePointSetting = (id, field, value) => {
setPointSettings((prev) => setPointSettings((prev) =>
prev.map((item) => (item.id === id ? { ...item, [field]: value } : item)) prev.map((item) => (item.id === id ? { ...item, [field]: value } : item))
) )
} }
// /**
* 保存配置到设备
*
* @description
* 将前端配置数据转换为设备需要的格式并发送到设备端
*
* @数据转换
* 前端格式{ id, pos: "点位1", xLen, yLen, enable }
* 设备格式{ type: "median", sensors: [{ pos: "1", xLen, yLen, enable }] }
*/
const handleSave = async () => { const handleSave = async () => {
if (!connectedDevice) { if (!connectedDevice) {
message.error('设备未连接') message.error('设备未连接')
@ -84,8 +177,8 @@ function AlgorithmSettings({ visible, onClose }) {
sensors: pointSettings.map((setting) => ({ sensors: pointSettings.map((setting) => ({
enable: setting.enable, enable: setting.enable,
pos: setting.pos.replace('点位', ''), // "1""1" pos: setting.pos.replace('点位', ''), // "1""1"
xLen: setting.xLen, xLen: setting.xLen || 0,
yLen: setting.yLen yLen: setting.yLen || 0
})) }))
} }
@ -124,6 +217,17 @@ function AlgorithmSettings({ visible, onClose }) {
</Button> </Button>
]} ]}
> >
<div style={{
padding: '12px',
marginBottom: '16px',
backgroundColor: '#e6f4ff',
border: '1px solid #91caff',
borderRadius: '4px',
color: '#0958d9',
fontSize: '14px'
}}>
💡 <strong>配置提示</strong>窗口大小X/Y请输入一个大于或等于3的奇数输入0或1表示该方向不启用中值滤波
</div>
<List <List
itemLayout="vertical" itemLayout="vertical"
dataSource={pointSettings} dataSource={pointSettings}
@ -134,27 +238,92 @@ function AlgorithmSettings({ visible, onClose }) {
<Flex vertical gap={12}> <Flex vertical gap={12}>
<Flex align="center" gap={8}> <Flex align="center" gap={8}>
<span style={{ minWidth: 100 }}>X方向:</span> <span style={{ minWidth: 100 }}>X方向:</span>
{/*
输入验证策略
1. onChange: 不验证直接更新 state支持多位数输入如输入21时先输入2再输入1
2. onBlur: 失去焦点时验证不合法则提示并清空
这样可以避免在输入过程中因为中间值不合法而导致输入被拦截
*/}
<Input <Input
type="number" type="number"
value={item.xLen} value={item.xLen}
onChange={(e) => min={0}
updatePointSetting(item.id, 'xLen', Number(e.target.value)) max={50}
} placeholder="0/1或>=3的奇数"
onChange={(e) => {
// onChange
const value = e.target.value === '' ? null : Number(e.target.value)
setPointSettings((prev) =>
prev.map((setting) =>
setting.id === item.id ? { ...setting, xLen: value } : setting
)
)
}}
onBlur={(e) => {
// onBlur
const value = e.target.value === '' ? null : Number(e.target.value)
if (value !== null && !validateWindowSize(value)) {
const num = Number(value)
if (num > 50) {
message.warning('窗口大小不能超过50')
} else if (num < 0) {
message.warning('窗口大小不能为负数')
} else {
message.warning('窗口大小必须是大于等于3的奇数,或者输入0/1表示不启用')
}
// null
setPointSettings((prev) =>
prev.map((setting) =>
setting.id === item.id ? { ...setting, xLen: null } : setting
)
)
}
}}
style={{ width: 150 }} style={{ width: 150 }}
/> />
</Flex> </Flex>
<Flex align="center" gap={8}> <Flex align="center" gap={8}>
<span style={{ minWidth: 100 }}>Y方向:</span> <span style={{ minWidth: 100 }}>Y方向:</span>
{/* Y方向使用与X方向相同的验证策略 */}
<Input <Input
type="number" type="number"
value={item.yLen} value={item.yLen}
onChange={(e) => max={50}
updatePointSetting(item.id, 'yLen', Number(e.target.value)) min={0}
} placeholder="0/1或>=3的奇数"
onChange={(e) => {
// onChange
const value = e.target.value === '' ? null : Number(e.target.value)
setPointSettings((prev) =>
prev.map((setting) =>
setting.id === item.id ? { ...setting, yLen: value } : setting
)
)
}}
onBlur={(e) => {
// onBlur
const value = e.target.value === '' ? null : Number(e.target.value)
if (value !== null && !validateWindowSize(value)) {
const num = Number(value)
if (num > 50) {
message.warning('窗口大小不能超过50')
} else if (num < 0) {
message.warning('窗口大小不能为负数')
} else {
message.warning('窗口大小必须是大于等于3的奇数,或者输入0/1表示不启用')
}
// null
setPointSettings((prev) =>
prev.map((setting) =>
setting.id === item.id ? { ...setting, yLen: null } : setting
)
)
}
}}
style={{ width: 150 }} style={{ width: 150 }}
/> />
</Flex> </Flex>
<Flex align="center" gap={8} justify="space-between"> <Flex align="center" gap={8}>
<span>是否启用中值滤波算法:</span> <span>是否启用中值滤波算法:</span>
<Switch <Switch
checked={item.enable} checked={item.enable}

Loading…
Cancel
Save