feat: 添加库位输入框及变化监听,自动运行任务
This commit is contained in:
parent
02a326e60e
commit
726b7db95f
@ -1,26 +1,80 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import { StyleSheet, ScrollView, TouchableOpacity } from 'react-native';
|
import { StyleSheet, ScrollView, TouchableOpacity } from 'react-native';
|
||||||
import { Input, BottomSheet, ListItem } from '@rneui/themed';
|
import { Input, BottomSheet, ListItem } from '@rneui/themed';
|
||||||
import { Task, RobotAction } from '../types/task';
|
import { Task, RobotAction } from '../types/task';
|
||||||
import {
|
import { LOCATIONS, ROBOT_ACTIONS, PAYLOADS } from '../data/mockData';
|
||||||
LOCATIONS,
|
|
||||||
ROBOT_ACTIONS,
|
|
||||||
PAYLOADS,
|
|
||||||
LOCATIONS_BAYS,
|
|
||||||
} from '../data/mockData';
|
|
||||||
|
|
||||||
interface TaskFormProps {
|
interface TaskFormProps {
|
||||||
task: Task;
|
task: Task;
|
||||||
onTaskChange: (updatedTask: Task) => void;
|
onTaskChange: (updatedTask: Task) => void;
|
||||||
|
onLocationBayChange?: (task: Task) => void; // 库位变化时的回调
|
||||||
}
|
}
|
||||||
|
|
||||||
const TaskForm: React.FC<TaskFormProps> = ({ task, onTaskChange }) => {
|
const TaskForm: React.FC<TaskFormProps> = ({
|
||||||
|
task,
|
||||||
|
onTaskChange,
|
||||||
|
onLocationBayChange,
|
||||||
|
}) => {
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
const [currentField, setCurrentField] = useState('');
|
const [currentField, setCurrentField] = useState('');
|
||||||
const [currentItems, setCurrentItems] = useState<
|
const [currentItems, setCurrentItems] = useState<
|
||||||
{ label: string; value: string }[]
|
{ label: string; value: string }[]
|
||||||
>([]);
|
>([]);
|
||||||
|
|
||||||
|
// 创建库位输入框的ref
|
||||||
|
const locationBayInputRef = useRef<any>(null);
|
||||||
|
// 用于防抖的ref
|
||||||
|
const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
// 保存上一次的库位值,用于检测变化
|
||||||
|
const previousLocationBayRef = useRef<string>(
|
||||||
|
task.parameters.locationBay || '',
|
||||||
|
);
|
||||||
|
|
||||||
|
// 组件挂载时自动聚焦到库位输入框
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
if (locationBayInputRef.current) {
|
||||||
|
locationBayInputRef.current.focus();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// 监听库位值变化,自动触发运行任务
|
||||||
|
useEffect(() => {
|
||||||
|
const currentLocationBay = task.parameters.locationBay || '';
|
||||||
|
const previousLocationBay = previousLocationBayRef.current;
|
||||||
|
|
||||||
|
// 只有当库位值真正发生变化且不为空时才触发
|
||||||
|
if (
|
||||||
|
currentLocationBay !== previousLocationBay &&
|
||||||
|
currentLocationBay.trim() !== ''
|
||||||
|
) {
|
||||||
|
// 清除之前的防抖定时器
|
||||||
|
if (debounceTimerRef.current) {
|
||||||
|
clearTimeout(debounceTimerRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置新的防抖定时器,500ms后触发
|
||||||
|
debounceTimerRef.current = setTimeout(() => {
|
||||||
|
if (onLocationBayChange) {
|
||||||
|
onLocationBayChange(task);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新上一次的库位值
|
||||||
|
previousLocationBayRef.current = currentLocationBay;
|
||||||
|
|
||||||
|
// 清理函数
|
||||||
|
return () => {
|
||||||
|
if (debounceTimerRef.current) {
|
||||||
|
clearTimeout(debounceTimerRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [task.parameters.locationBay, task, onLocationBayChange]);
|
||||||
|
|
||||||
const handleParamChange = (field: string, value: string | RobotAction) => {
|
const handleParamChange = (field: string, value: string | RobotAction) => {
|
||||||
const updatedTask = {
|
const updatedTask = {
|
||||||
...task,
|
...task,
|
||||||
@ -91,12 +145,17 @@ const TaskForm: React.FC<TaskFormProps> = ({ task, onTaskChange }) => {
|
|||||||
ROBOT_ACTIONS,
|
ROBOT_ACTIONS,
|
||||||
)}
|
)}
|
||||||
{renderDropdown('payload', '载荷', task.parameters.payload, PAYLOADS)}
|
{renderDropdown('payload', '载荷', task.parameters.payload, PAYLOADS)}
|
||||||
{renderDropdown(
|
|
||||||
'locationBay',
|
{/* 库位字段改为普通输入框 */}
|
||||||
'库位',
|
<Input
|
||||||
task.parameters.locationBay || '',
|
ref={locationBayInputRef}
|
||||||
LOCATIONS_BAYS,
|
label="库位"
|
||||||
)}
|
value={task.parameters.locationBay || ''}
|
||||||
|
onChangeText={text => handleParamChange('locationBay', text)}
|
||||||
|
showSoftInputOnFocus={false}
|
||||||
|
autoFocus={false}
|
||||||
|
placeholder="请输入库位"
|
||||||
|
/>
|
||||||
|
|
||||||
<BottomSheet
|
<BottomSheet
|
||||||
isVisible={isVisible}
|
isVisible={isVisible}
|
||||||
|
@ -2,8 +2,8 @@ import { Task, RobotAction } from '../types/task';
|
|||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
export const LOCATIONS = Array.from({ length: 100 }, (_, i) => ({
|
export const LOCATIONS = Array.from({ length: 100 }, (_, i) => ({
|
||||||
label: `AP-${i + 1}`,
|
label: `AP-${i + 1}`,
|
||||||
value: `AP-${i + 1}`,
|
value: `AP-${i + 1}`,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const ROBOT_ACTIONS: { label: string; value: RobotAction }[] = [
|
export const ROBOT_ACTIONS: { label: string; value: RobotAction }[] = [
|
||||||
@ -16,38 +16,48 @@ export const ROBOT_ACTIONS: { label: string; value: RobotAction }[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const LOCATIONS_BAYS = Array.from({ length: 100 }, (_, i) => ({
|
export const LOCATIONS_BAYS = Array.from({ length: 100 }, (_, i) => ({
|
||||||
label: `AS2_2_${String(i + 1).padStart(3, '0')}`,
|
label: `AS2_2_${String(i + 1).padStart(3, '0')}`,
|
||||||
value: `AS2_2_${String(i + 1).padStart(3, '0')}`,
|
value: `AS2_2_${String(i + 1).padStart(3, '0')}`,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const PAYLOADS = [
|
export const PAYLOADS = [
|
||||||
{ label: '满料架-A1', value: '满料架-A1' },
|
{ label: '满料架-A1', value: '满料架-A1' },
|
||||||
{ label: '空料架-B2', value: '空料架-B2' },
|
{ label: '空料架-B2', value: '空料架-B2' },
|
||||||
{ label: '空料架-C3', value: '空料架-C3' },
|
{ label: '空料架-C3', value: '空料架-C3' },
|
||||||
{ label: '空车', value: '空车' },
|
{ label: '空车', value: '空车' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const getRandomElement = <T>(arr: T[]): T => arr[Math.floor(Math.random() * arr.length)];
|
const getRandomElement = <T>(arr: T[]): T =>
|
||||||
|
arr[Math.floor(Math.random() * arr.length)];
|
||||||
|
|
||||||
export const MOCK_TASKS: Task[] = Array.from({ length: 15 }, (_, i) => {
|
export const MOCK_TASKS: Task[] = Array.from({ length: 15 }, (_, i) => {
|
||||||
const startLocation = getRandomElement(LOCATIONS);
|
const startLocation = getRandomElement(LOCATIONS);
|
||||||
const endLocation = getRandomElement(LOCATIONS.filter(l => l.value !== startLocation.value));
|
const endLocation = getRandomElement(
|
||||||
const robotAction = getRandomElement(ROBOT_ACTIONS);
|
LOCATIONS.filter(l => l.value !== startLocation.value),
|
||||||
const payload = getRandomElement(PAYLOADS);
|
);
|
||||||
const statusOptions: Task['status'][] = ['IDLE', 'RUNNING', 'COMPLETED', 'ERROR'];
|
const robotAction = getRandomElement(ROBOT_ACTIONS);
|
||||||
const status = getRandomElement(statusOptions);
|
const payload = getRandomElement(PAYLOADS);
|
||||||
|
const statusOptions: Task['status'][] = [
|
||||||
|
'IDLE',
|
||||||
|
'RUNNING',
|
||||||
|
'COMPLETED',
|
||||||
|
'ERROR',
|
||||||
|
];
|
||||||
|
const status = getRandomElement(statusOptions);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
name: `任务 ${i + 1}: 从 ${startLocation.label} 到 ${endLocation.label}`,
|
name: `任务 ${i + 1}: 从 ${startLocation.label} 到 ${endLocation.label}`,
|
||||||
status: status,
|
status: status,
|
||||||
createdAt: new Date(new Date().getTime() - Math.random() * 1000 * 60 * 60 * 24).toISOString(),
|
createdAt: new Date(
|
||||||
parameters: {
|
new Date().getTime() - Math.random() * 1000 * 60 * 60 * 24,
|
||||||
startLocation: startLocation.value,
|
).toISOString(),
|
||||||
endLocation: endLocation.value,
|
parameters: {
|
||||||
robotAction: robotAction.value,
|
startLocation: startLocation.value,
|
||||||
payload: payload.value,
|
endLocation: endLocation.value,
|
||||||
locationBay: getRandomElement(LOCATIONS_BAYS).value,
|
robotAction: robotAction.value,
|
||||||
},
|
payload: payload.value,
|
||||||
};
|
locationBay: '', // 库位初始值为空
|
||||||
|
},
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
@ -32,7 +32,6 @@ export default function TaskEditScreen() {
|
|||||||
}
|
}
|
||||||
}, [initialTask]);
|
}, [initialTask]);
|
||||||
|
|
||||||
|
|
||||||
const handleTaskChange = (updatedTask: Task) => {
|
const handleTaskChange = (updatedTask: Task) => {
|
||||||
setTask(updatedTask);
|
setTask(updatedTask);
|
||||||
if (!isModified) {
|
if (!isModified) {
|
||||||
@ -48,35 +47,48 @@ export default function TaskEditScreen() {
|
|||||||
Alert.alert('已保存', `任务 "${task.name}" 已被保存。`);
|
Alert.alert('已保存', `任务 "${task.name}" 已被保存。`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRun = () => {
|
const handleRun = () => {
|
||||||
if(task) {
|
if (task) {
|
||||||
runTask(task.id);
|
runTask(task.id);
|
||||||
navigation.goBack();
|
navigation.goBack();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleUndo = () => {
|
const handleUndo = () => {
|
||||||
setTask(originalTask);
|
setTask(originalTask);
|
||||||
setIsModified(false);
|
setIsModified(false);
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleRestore = () => {
|
const handleRestore = () => {
|
||||||
setTask(originalTask);
|
setTask(originalTask);
|
||||||
setIsModified(false);
|
setIsModified(false);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// 处理库位变化,自动运行任务
|
||||||
|
const handleLocationBayChange = (updatedTask: Task) => {
|
||||||
|
console.log(
|
||||||
|
'库位已变化,自动运行任务:',
|
||||||
|
updatedTask.parameters.locationBay,
|
||||||
|
);
|
||||||
|
runTask(updatedTask.id);
|
||||||
|
};
|
||||||
|
|
||||||
if (!task) {
|
if (!task) {
|
||||||
return (
|
return (
|
||||||
<Dialog isVisible={true}>
|
<Dialog isVisible={true}>
|
||||||
<Dialog.Loading />
|
<Dialog.Loading />
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<TaskForm task={task} onTaskChange={handleTaskChange} />
|
<TaskForm
|
||||||
|
task={task}
|
||||||
|
onTaskChange={handleTaskChange}
|
||||||
|
onLocationBayChange={handleLocationBayChange}
|
||||||
|
/>
|
||||||
<BottomActionBar
|
<BottomActionBar
|
||||||
onRun={handleRun}
|
onRun={handleRun}
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
// 机器人的具体动作,可以定义为枚举或联合类型
|
// 机器人的具体动作,可以定义为枚举或联合类型
|
||||||
export type RobotAction = 'PICKUP' | 'DROPOFF' | 'TRANSPORT' | 'WAIT';
|
export type RobotAction =
|
||||||
|
| 'PICKUP'
|
||||||
|
| 'DROPOFF'
|
||||||
|
| 'TRANSPORT'
|
||||||
|
| 'WAIT'
|
||||||
|
| 'CHARGE'
|
||||||
|
| 'CLEAN';
|
||||||
|
|
||||||
// 任务的状态
|
// 任务的状态
|
||||||
export type TaskStatus = 'IDLE' | 'RUNNING' | 'COMPLETED' | 'ERROR';
|
export type TaskStatus = 'IDLE' | 'RUNNING' | 'COMPLETED' | 'ERROR';
|
||||||
@ -7,11 +13,11 @@ export type TaskStatus = 'IDLE' | 'RUNNING' | 'COMPLETED' | 'ERROR';
|
|||||||
// 任务参数
|
// 任务参数
|
||||||
export interface TaskParameters {
|
export interface TaskParameters {
|
||||||
startLocation: string; // 起点
|
startLocation: string; // 起点
|
||||||
endLocation: string; // 终点
|
endLocation: string; // 终点
|
||||||
waypoint?: string; // 途经点 (可选)
|
waypoint?: string; // 途经点 (可选)
|
||||||
robotAction: RobotAction; // 机器人动作
|
robotAction: RobotAction; // 机器人动作
|
||||||
payload: string; // 载荷,比如 '空料架' 或具体的物料ID
|
payload: string; // 载荷,比如 '空料架' 或具体的物料ID
|
||||||
locationBay?: string; // 库位 (可选)
|
locationBay?: string; // 库位 (可选)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 核心任务对象
|
// 核心任务对象
|
||||||
|
Loading…
x
Reference in New Issue
Block a user