diff --git a/App.tsx b/App.tsx
index f1e3eb1..1285d43 100644
--- a/App.tsx
+++ b/App.tsx
@@ -2,14 +2,17 @@ import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import AppNavigator from './src/navigation/AppNavigator';
+import { TasksProvider } from './src/context/TasksContext';
import { PaperProvider } from 'react-native-paper';
export default function App() {
return (
-
-
-
+
+
+
+
+
);
}
diff --git a/package-lock.json b/package-lock.json
index 6678b0c..de3642f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,6 +17,7 @@
"react-native-gesture-handler": "^2.27.1",
"react-native-get-random-values": "^1.11.0",
"react-native-paper": "^5.14.5",
+ "react-native-paper-dropdown": "^2.3.1",
"react-native-safe-area-context": "^5.5.2",
"react-native-screens": "^4.13.1",
"react-native-vector-icons": "^10.2.0",
@@ -10826,6 +10827,23 @@
"react-native-safe-area-context": "*"
}
},
+ "node_modules/react-native-paper-dropdown": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmmirror.com/react-native-paper-dropdown/-/react-native-paper-dropdown-2.3.1.tgz",
+ "integrity": "sha512-IvcHTucAV5+fiX2IVMiVdBDKT6KHxycW0o9QzZe7bpmeZWmuCajHDnwG3OSBGlXhUxrrM3TC0/HJZHwORWGgQg==",
+ "license": "MIT",
+ "workspaces": [
+ "example"
+ ],
+ "dependencies": {
+ "react-native-paper": "^5.12.3"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*",
+ "react-native-paper": "*"
+ }
+ },
"node_modules/react-native-paper/node_modules/color": {
"version": "3.2.1",
"resolved": "https://registry.npmmirror.com/color/-/color-3.2.1.tgz",
diff --git a/package.json b/package.json
index b75c2dc..a16a239 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"react-native-gesture-handler": "^2.27.1",
"react-native-get-random-values": "^1.11.0",
"react-native-paper": "^5.14.5",
+ "react-native-paper-dropdown": "^2.3.1",
"react-native-safe-area-context": "^5.5.2",
"react-native-screens": "^4.13.1",
"react-native-vector-icons": "^10.2.0",
diff --git a/src/components/BottomActionBar.tsx b/src/components/BottomActionBar.tsx
index 41c17a0..259970f 100644
--- a/src/components/BottomActionBar.tsx
+++ b/src/components/BottomActionBar.tsx
@@ -1,5 +1,6 @@
import React from 'react';
-import { View, Button, StyleSheet } from 'react-native';
+import { Appbar, useTheme } from 'react-native-paper';
+import { StyleSheet } from 'react-native';
interface BottomActionBarProps {
onRun: () => void;
@@ -18,25 +19,26 @@ const BottomActionBar: React.FC = ({
onBack,
isSaveDisabled = true,
}) => {
+ const theme = useTheme();
+
return (
-
-
-
-
-
-
-
+
+
+
+
+
+
+
);
};
const styles = StyleSheet.create({
- container: {
- flexDirection: 'row',
+ bottom: {
+ position: 'absolute',
+ left: 0,
+ right: 0,
+ bottom: 0,
justifyContent: 'space-around',
- padding: 16,
- borderTopWidth: 1,
- borderColor: '#ccc',
- backgroundColor: '#f5f5f5',
},
});
diff --git a/src/components/TaskCard.tsx b/src/components/TaskCard.tsx
index af9afbd..ab15b66 100644
--- a/src/components/TaskCard.tsx
+++ b/src/components/TaskCard.tsx
@@ -1,43 +1,52 @@
import React from 'react';
-import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
-import { Task } from '../types/task';
+import { StyleSheet } from 'react-native';
+import { Card, Text, Chip, useTheme } from 'react-native-paper';
+import { Task, TaskStatus } from '../types/task';
interface TaskCardProps {
task: Task;
onPress: (id: string) => void;
}
+const statusColors: Record = {
+ IDLE: 'grey',
+ RUNNING: 'blue',
+ COMPLETED: 'green',
+ ERROR: 'red',
+};
+
const TaskCard: React.FC = ({ task, onPress }) => {
+ const theme = useTheme();
+
return (
- onPress(task.id)}>
- {task.name}
- 状态: {task.status}
-
+ onPress(task.id)}>
+
+ {task.name}
+
+ {task.status}
+
+
+
);
};
const styles = StyleSheet.create({
card: {
- backgroundColor: '#fff',
- borderRadius: 8,
- padding: 16,
margin: 8,
width: '45%',
- elevation: 3,
- shadowColor: '#000',
- shadowOffset: { width: 0, height: 2 },
- shadowOpacity: 0.1,
- shadowRadius: 2,
},
title: {
- fontSize: 16,
- fontWeight: 'bold',
- marginBottom: 8,
- },
- status: {
- fontSize: 14,
- color: '#666',
+ marginBottom: 12,
+ minHeight: 50, // Ensure cards have similar height
},
+ chip: {
+ alignSelf: 'flex-start',
+ }
});
export default TaskCard;
diff --git a/src/components/TaskForm.tsx b/src/components/TaskForm.tsx
index 69d2d97..fe87ba0 100644
--- a/src/components/TaskForm.tsx
+++ b/src/components/TaskForm.tsx
@@ -1,6 +1,9 @@
-import React from 'react';
-import { View, Text, TextInput, StyleSheet, ScrollView } from 'react-native';
+import React, { useState } from 'react';
+import { View, StyleSheet, ScrollView } from 'react-native';
+import { TextInput } from 'react-native-paper';
+import DropDown from "react-native-paper-dropdown";
import { Task, RobotAction } from '../types/task';
+import { LOCATIONS, ROBOT_ACTIONS, PAYLOADS } from '../data/mockData';
interface TaskFormProps {
task: Task;
@@ -8,7 +11,12 @@ interface TaskFormProps {
}
const TaskForm: React.FC = ({ task, onTaskChange }) => {
- const handleParamChange = (field: string, value: string) => {
+ const [showStartLocation, setShowStartLocation] = useState(false);
+ const [showEndLocation, setShowEndLocation] = useState(false);
+ const [showRobotAction, setShowRobotAction] = useState(false);
+ const [showPayload, setShowPayload] = useState(false);
+
+ const handleParamChange = (field: string, value: string | RobotAction) => {
const updatedTask = {
...task,
parameters: {
@@ -20,85 +28,120 @@ const TaskForm: React.FC = ({ task, onTaskChange }) => {
};
return (
-
-
- 任务名称
+
onTaskChange({ ...task, name: text })}
+ label="任务名称"
+ value={task.name}
+ onChangeText={text => onTaskChange({ ...task, name: text })}
+ style={styles.input}
+ mode="outlined"
/>
-
-
- 起点
+ {/* setShowStartLocation(true)}
+ onDismiss={() => setShowStartLocation(false)}
+ value={task.parameters.startLocation}
+ setValue={(value) => handleParamChange('startLocation', value)}
+ list={LOCATIONS.map((location) => ({
+ label: location.label,
+ value: location.value,
+ }))}
+ dropDownStyle={styles.input}
+ /> */}
handleParamChange('startLocation', text)}
+ label="起点"
+ value={task.parameters.startLocation}
+ onChangeText={text => handleParamChange('startLocation', text)}
+ style={styles.input}
+ mode="outlined"
/>
-
-
- 终点
+ {/* setShowEndLocation(true)}
+ onDismiss={() => setShowEndLocation(false)}
+ value={task.parameters.endLocation}
+ setValue={(value) => handleParamChange('endLocation', value)}
+ list={LOCATIONS.map((location) => ({
+ label: location.label,
+ value: location.value,
+ }))}
+ dropDownStyle={styles.input}
+ /> */}
handleParamChange('endLocation', text)}
+ label="终点"
+ value={task.parameters.endLocation}
+ onChangeText={text => handleParamChange('endLocation', text)}
+ style={styles.input}
+ mode="outlined"
/>
-
-
-
- 途经点
+
handleParamChange('waypoint', text)}
- placeholder="可选"
+ label="途经点 (可选)"
+ value={task.parameters.waypoint || ''}
+ onChangeText={text => handleParamChange('waypoint', text)}
+ style={styles.input}
+ mode="outlined"
+ />
+
+ {/* setShowRobotAction(true)}
+ onDismiss={() => setShowRobotAction(false)}
+ value={task.parameters.robotAction}
+ setValue={(value) => handleParamChange('robotAction', value)}
+ list={ROBOT_ACTIONS.map((action) => ({
+ label: action.label,
+ value: action.value,
+ }))}
+ dropDownStyle={styles.input}
+ /> */}
+ handleParamChange('robotAction', text)}
+ style={styles.input}
+ mode="outlined"
/>
-
-
- 机器人动作
- {/* 在实际应用中,这里最好是一个下拉选择器 */}
+ {/* setShowPayload(true)}
+ onDismiss={() => setShowPayload(false)}
+ value={task.parameters.payload}
+ setValue={(value) => handleParamChange('payload', value)}
+ list={PAYLOADS.map((payload) => ({
+ label: payload.label,
+ value: payload.value,
+ }))}
+ dropDownStyle={styles.input}
+ /> */}
handleParamChange('robotAction', text as RobotAction)}
+ label="载荷"
+ value={task.parameters.payload}
+ onChangeText={text => handleParamChange('payload', text)}
+ style={styles.input}
+ mode="outlined"
/>
-
-
-
- 载荷
- handleParamChange('payload', text)}
- />
-
);
};
const styles = StyleSheet.create({
container: {
- flex: 1,
padding: 16,
},
- fieldContainer: {
- marginBottom: 16,
- },
- label: {
- fontSize: 16,
- marginBottom: 8,
- fontWeight: 'bold',
- },
input: {
- borderWidth: 1,
- borderColor: '#ccc',
- borderRadius: 4,
- padding: 12,
- fontSize: 16,
+ marginBottom: 16,
},
});
diff --git a/src/context/TasksContext.tsx b/src/context/TasksContext.tsx
new file mode 100644
index 0000000..c3bdbda
--- /dev/null
+++ b/src/context/TasksContext.tsx
@@ -0,0 +1,52 @@
+import React, { createContext, useState, useContext, ReactNode } from 'react';
+import { Task } from '../types/task';
+import { MOCK_TASKS } from '../data/mockData';
+
+interface TasksContextData {
+ tasks: Task[];
+ getTaskById: (id: string) => Task | undefined;
+ updateTask: (updatedTask: Task) => void;
+ runTask: (id: string) => void;
+}
+
+const TasksContext = createContext({} as TasksContextData);
+
+export const TasksProvider: React.FC<{children: ReactNode}> = ({ children }) => {
+ const [tasks, setTasks] = useState(MOCK_TASKS);
+
+ const getTaskById = (id: string) => {
+ return tasks.find(task => task.id === id);
+ };
+
+ const updateTask = (updatedTask: Task) => {
+ setTasks(prevTasks =>
+ prevTasks.map(task => (task.id === updatedTask.id ? updatedTask : task))
+ );
+ };
+
+ const runTask = (id: string) => {
+ setTasks(prevTasks =>
+ prevTasks.map(task =>
+ task.id === id ? { ...task, status: 'RUNNING' } : task
+ )
+ );
+ // 模拟任务完成
+ setTimeout(() => {
+ setTasks(prevTasks =>
+ prevTasks.map(task =>
+ task.id === id ? { ...task, status: 'COMPLETED' } : task
+ )
+ );
+ }, 5000);
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+export function useTasks() {
+ return useContext(TasksContext);
+}
diff --git a/src/data/mockData.ts b/src/data/mockData.ts
index d7f84e0..44838aa 100644
--- a/src/data/mockData.ts
+++ b/src/data/mockData.ts
@@ -1,6 +1,30 @@
-import { Task } from '../types/task';
+import { Task, RobotAction } from '../types/task';
import { v4 as uuidv4 } from 'uuid';
+export const LOCATIONS = [
+ { label: '炉前缓存区', value: '炉前缓存区' },
+ { label: '热处理上料交接区', value: '热处理上料交接区' },
+ { label: '空料架缓存区', value: '空料架缓存区' },
+ { label: '焊接空料架交接区', value: '焊接空料架交接区' },
+ { label: '热后区', value: '热后区' },
+ { label: 'ALD上料区', value: 'ALD上料区' },
+ { label: '空车区', value: '空车区' },
+];
+
+export const ROBOT_ACTIONS: { label: string; value: RobotAction }[] = [
+ { label: '运输', value: 'TRANSPORT' },
+ { label: '取货', value: 'PICKUP' },
+ { label: '卸货', value: 'DROPOFF' },
+ { label: '等待', value: 'WAIT' },
+];
+
+export const PAYLOADS = [
+ { label: '满料架-A1', value: '满料架-A1' },
+ { label: '空料架-B2', value: '空料架-B2' },
+ { label: '空料架-C3', value: '空料架-C3' },
+ { label: '空车', value: '空车' },
+];
+
export const MOCK_TASKS: Task[] = [
{
id: uuidv4(),
diff --git a/src/screens/TaskEditScreen.tsx b/src/screens/TaskEditScreen.tsx
index b1a8441..5ff157f 100644
--- a/src/screens/TaskEditScreen.tsx
+++ b/src/screens/TaskEditScreen.tsx
@@ -2,11 +2,11 @@ import React, { useState, useEffect } from 'react';
import { View, StyleSheet, Alert } from 'react-native';
import { useRoute, useNavigation } from '@react-navigation/native';
import { RouteProp } from '@react-navigation/native';
+import { useTasks } from '../context/TasksContext';
import TaskForm from '../components/TaskForm';
import BottomActionBar from '../components/BottomActionBar';
import { Task } from '../types/task';
-
-import { MOCK_TASKS } from '../data/mockData';
+import { ActivityIndicator } from 'react-native-paper';
type RootStackParamList = {
TaskEdit: { taskId: string };
@@ -19,16 +19,19 @@ export default function TaskEditScreen() {
const navigation = useNavigation();
const { taskId } = route.params;
+ const { getTaskById, updateTask, runTask } = useTasks();
+
const [task, setTask] = useState(null);
+ const [originalTask, setOriginalTask] = useState(null);
const [isModified, setIsModified] = useState(false);
useEffect(() => {
- // 根据taskId从模拟数据中查找任务
- const foundTask = MOCK_TASKS.find(t => t.id === taskId);
+ const foundTask = getTaskById(taskId);
if (foundTask) {
setTask(foundTask);
+ setOriginalTask(foundTask);
}
- }, [taskId]);
+ }, [taskId, getTaskById]);
const handleTaskChange = (updatedTask: Task) => {
setTask(updatedTask);
@@ -38,23 +41,43 @@ export default function TaskEditScreen() {
};
const handleSave = () => {
- // 在这里处理保存逻辑,比如调用API
- Alert.alert('已保存', `任务 "${task?.name}" 已被保存。`);
- setIsModified(false);
+ if (task) {
+ updateTask(task);
+ setOriginalTask(task);
+ setIsModified(false);
+ Alert.alert('已保存', `任务 "${task.name}" 已被保存。`);
+ }
};
+
+ const handleRun = () => {
+ if(task) {
+ runTask(task.id);
+ navigation.goBack();
+ }
+ }
+
+ const handleUndo = () => {
+ setTask(originalTask);
+ setIsModified(false);
+ }
+
+ const handleRestore = () => {
+ setTask(originalTask);
+ setIsModified(false);
+ }
if (!task) {
- return null; // 或者显示一个加载指示器
+ return ;
}
return (
Alert.alert('运行', '开始运行任务...')}
+ onRun={handleRun}
onSave={handleSave}
- onUndo={() => Alert.alert('撤销', '撤销更改...')}
- onRestore={() => Alert.alert('恢复', '恢复到初始状态...')}
+ onUndo={handleUndo}
+ onRestore={handleRestore}
onBack={() => navigation.goBack()}
isSaveDisabled={!isModified}
/>
@@ -67,4 +90,9 @@ const styles = StyleSheet.create({
flex: 1,
backgroundColor: '#fff',
},
+ loader: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center'
+ }
});
diff --git a/src/screens/TaskListScreen.tsx b/src/screens/TaskListScreen.tsx
index 2910fb8..83c438b 100644
--- a/src/screens/TaskListScreen.tsx
+++ b/src/screens/TaskListScreen.tsx
@@ -1,10 +1,9 @@
-import React, { useState } from 'react';
+import React from 'react';
import { View, FlatList, StyleSheet } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { StackNavigationProp } from '@react-navigation/stack';
+import { useTasks } from '../context/TasksContext';
import TaskCard from '../components/TaskCard';
-import { Task } from '../types/task';
-import { MOCK_TASKS } from '../data/mockData';
type RootStackParamList = {
TaskList: undefined;
@@ -14,7 +13,7 @@ type RootStackParamList = {
type TaskListNavigationProp = StackNavigationProp;
export default function TaskListScreen() {
- const [tasks] = useState(MOCK_TASKS);
+ const { tasks } = useTasks();
const navigation = useNavigation();
const handlePressTask = (taskId: string) => {
@@ -28,7 +27,7 @@ export default function TaskListScreen() {
renderItem={({ item }) => }
keyExtractor={item => item.id}
numColumns={2}
- columnWrapperStyle={styles.row}
+ contentContainerStyle={styles.list}
/>
);
@@ -37,9 +36,8 @@ export default function TaskListScreen() {
const styles = StyleSheet.create({
container: {
flex: 1,
- backgroundColor: '#f5f5f5',
- },
- row: {
- justifyContent: 'center',
},
+ list: {
+ padding: 8,
+ }
});