Compare commits
No commits in common. "508780d6340d4ac75495bc4669fc38f969919268" and "ce11581196e05c75ae8b879c32605436be2039c6" have entirely different histories.
508780d634
...
ce11581196
13
App.tsx
13
App.tsx
@ -1,22 +1,11 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React from 'react';
|
||||||
import { NavigationContainer } from '@react-navigation/native';
|
import { NavigationContainer } from '@react-navigation/native';
|
||||||
import AppNavigator from './src/navigation/AppNavigator';
|
import AppNavigator from './src/navigation/AppNavigator';
|
||||||
|
|
||||||
import { TasksProvider } from './src/context/TasksContext';
|
import { TasksProvider } from './src/context/TasksContext';
|
||||||
import { ThemeProvider } from '@rneui/themed';
|
import { ThemeProvider } from '@rneui/themed';
|
||||||
import { clearCachedConfig } from './src/services/configService';
|
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
useEffect(() => {
|
|
||||||
const clearCacheOnStart = async () => {
|
|
||||||
console.log('正在清除缓存的配置文件...');
|
|
||||||
await clearCachedConfig();
|
|
||||||
console.log('缓存已清除,将使用最新的 config.json。');
|
|
||||||
};
|
|
||||||
|
|
||||||
clearCacheOnStart();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<TasksProvider>
|
<TasksProvider>
|
||||||
|
42
config.json
42
config.json
@ -1,13 +1,45 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"locations": [
|
||||||
|
{ "label": "AP-1", "value": "AP-1" },
|
||||||
|
{ "label": "AP-2", "value": "AP-2" },
|
||||||
|
{ "label": "AP-3", "value": "AP-3" },
|
||||||
|
{ "label": "AP-4", "value": "AP-4" },
|
||||||
|
{ "label": "AP-5", "value": "AP-5" },
|
||||||
|
{ "label": "炉前缓存区", "value": "FURNACE_BUFFER" },
|
||||||
|
{ "label": "热处理上料交接区", "value": "HEAT_TREATMENT_LOADING" },
|
||||||
|
{ "label": "成品存储区", "value": "FINISHED_STORAGE" },
|
||||||
|
{ "label": "原料仓库", "value": "RAW_MATERIAL_WAREHOUSE" },
|
||||||
|
{ "label": "质检区", "value": "QUALITY_INSPECTION" }
|
||||||
|
],
|
||||||
|
"locationsBays": [
|
||||||
|
{ "label": "AS2_2_001", "value": "AS2_2_001" },
|
||||||
|
{ "label": "AS2_2_002", "value": "AS2_2_002" },
|
||||||
|
{ "label": "AS2_2_003", "value": "AS2_2_003" },
|
||||||
|
{ "label": "AS2_2_004", "value": "AS2_2_004" },
|
||||||
|
{ "label": "AS2_2_005", "value": "AS2_2_005" }
|
||||||
|
],
|
||||||
|
"payloads": [
|
||||||
|
{ "label": "满料架-A1", "value": "满料架-A1" },
|
||||||
|
{ "label": "满料架-A2", "value": "满料架-A2" },
|
||||||
|
{ "label": "空料架-B1", "value": "空料架-B1" },
|
||||||
|
{ "label": "空料架-B2", "value": "空料架-B2" },
|
||||||
|
{ "label": "空车", "value": "空车" },
|
||||||
|
{ "label": "钢材-Q235", "value": "钢材-Q235" },
|
||||||
|
{ "label": "铝合金-6061", "value": "铝合金-6061" }
|
||||||
|
],
|
||||||
|
"robotActions": [
|
||||||
|
{ "label": "运输", "value": "TRANSPORT" },
|
||||||
|
{ "label": "取货", "value": "PICKUP" },
|
||||||
|
{ "label": "卸货", "value": "DROPOFF" },
|
||||||
|
{ "label": "等待", "value": "WAIT" },
|
||||||
|
{ "label": "充电", "value": "CHARGE" },
|
||||||
|
{ "label": "清洁", "value": "CLEAN" }
|
||||||
|
],
|
||||||
"serverUrl": "http://192.168.189.206:8000",
|
"serverUrl": "http://192.168.189.206:8000",
|
||||||
"headers": {
|
|
||||||
"x-access-token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NTIzMzQ5MjEsInVzZXJuYW1lIjoiYWRtaW4ifQ.dKvKIJOU5FxFeFNI3ucJTPLhHmUFZkv8HA2S_VO6klY",
|
|
||||||
"x-tenant-id": 1000
|
|
||||||
},
|
|
||||||
"apiEndpoints": {
|
"apiEndpoints": {
|
||||||
"getTasks": "/api/vwed-task/list",
|
"getTasks": "/api/vwed-task/list",
|
||||||
"getTaskDetail": "/api/vwed-task/{taskId}",
|
"getTaskDetail": "/api/vwed-task/{taskId}",
|
||||||
"runTask": "/api/vwed-task-edit/run"
|
"runTask": "/api/vwed-task/execute/{taskId}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
package-lock.json
generated
10
package-lock.json
generated
@ -16,7 +16,7 @@
|
|||||||
"@react-navigation/stack": "^7.4.2",
|
"@react-navigation/stack": "^7.4.2",
|
||||||
"@rneui/base": "^4.0.0-rc.8",
|
"@rneui/base": "^4.0.0-rc.8",
|
||||||
"@rneui/themed": "^4.0.0-rc.8",
|
"@rneui/themed": "^4.0.0-rc.8",
|
||||||
"axios": "^1.11.0",
|
"axios": "^1.10.0",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-native": "0.80.1",
|
"react-native": "0.80.1",
|
||||||
"react-native-gesture-handler": "^2.27.1",
|
"react-native-gesture-handler": "^2.27.1",
|
||||||
@ -4419,13 +4419,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.11.0",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.11.0.tgz",
|
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.10.0.tgz",
|
||||||
"integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
|
"integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.6",
|
"follow-redirects": "^1.15.6",
|
||||||
"form-data": "^4.0.4",
|
"form-data": "^4.0.0",
|
||||||
"proxy-from-env": "^1.1.0"
|
"proxy-from-env": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
"@react-navigation/stack": "^7.4.2",
|
"@react-navigation/stack": "^7.4.2",
|
||||||
"@rneui/base": "^4.0.0-rc.8",
|
"@rneui/base": "^4.0.0-rc.8",
|
||||||
"@rneui/themed": "^4.0.0-rc.8",
|
"@rneui/themed": "^4.0.0-rc.8",
|
||||||
"axios": "^1.11.0",
|
"axios": "^1.10.0",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-native": "0.80.1",
|
"react-native": "0.80.1",
|
||||||
"react-native-gesture-handler": "^2.27.1",
|
"react-native-gesture-handler": "^2.27.1",
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import { View, StyleSheet, TouchableOpacity, Text } from 'react-native';
|
||||||
View,
|
|
||||||
StyleSheet,
|
|
||||||
TouchableOpacity,
|
|
||||||
Text,
|
|
||||||
ActivityIndicator,
|
|
||||||
} from 'react-native';
|
|
||||||
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
|
|
||||||
interface BottomActionBarProps {
|
interface BottomActionBarProps {
|
||||||
@ -14,7 +8,6 @@ interface BottomActionBarProps {
|
|||||||
onUndo: () => void;
|
onUndo: () => void;
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
isSaveDisabled?: boolean;
|
isSaveDisabled?: boolean;
|
||||||
isRunLoading?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const BottomActionBar: React.FC<BottomActionBarProps> = ({
|
const BottomActionBar: React.FC<BottomActionBarProps> = ({
|
||||||
@ -23,7 +16,6 @@ const BottomActionBar: React.FC<BottomActionBarProps> = ({
|
|||||||
onUndo,
|
onUndo,
|
||||||
onBack,
|
onBack,
|
||||||
isSaveDisabled = true,
|
isSaveDisabled = true,
|
||||||
isRunLoading = false,
|
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
@ -56,21 +48,10 @@ const BottomActionBar: React.FC<BottomActionBarProps> = ({
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={onRun}
|
onPress={onRun}
|
||||||
disabled={isRunLoading}
|
style={[styles.button, styles.runButton]}
|
||||||
style={[
|
|
||||||
styles.button,
|
|
||||||
styles.runButton,
|
|
||||||
isRunLoading && styles.disabledButton,
|
|
||||||
]}
|
|
||||||
>
|
>
|
||||||
{isRunLoading ? (
|
<Icon name="play" size={24} color="#FFFFFF" />
|
||||||
<ActivityIndicator size="small" color="#FFFFFF" />
|
<Text style={[styles.buttonText, styles.runButtonText]}>运行</Text>
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Icon name="play" size={24} color="#FFFFFF" />
|
|
||||||
<Text style={[styles.buttonText, styles.runButtonText]}>运行</Text>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet, Text, View } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
import { Input, Slider } from '@rneui/themed';
|
import { Input, Slider } from '@rneui/themed';
|
||||||
|
// @ts-ignore
|
||||||
|
import { Picker } from '@react-native-picker/picker';
|
||||||
|
import { useTasks } from '../context/TasksContext';
|
||||||
import { Task, RobotAction, InputParam } from '../types/task';
|
import { Task, RobotAction, InputParam } from '../types/task';
|
||||||
|
|
||||||
interface TaskFormProps {
|
interface TaskFormProps {
|
||||||
@ -9,6 +12,8 @@ interface TaskFormProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const TaskForm: React.FC<TaskFormProps> = ({ task, onTaskChange }) => {
|
const TaskForm: React.FC<TaskFormProps> = ({ task, onTaskChange }) => {
|
||||||
|
const { locations, payloads, robotActions, locationsBays } = useTasks();
|
||||||
|
|
||||||
const handleParamChange = (
|
const handleParamChange = (
|
||||||
field: string,
|
field: string,
|
||||||
value: string | number | RobotAction,
|
value: string | number | RobotAction,
|
||||||
@ -53,6 +58,92 @@ const TaskForm: React.FC<TaskFormProps> = ({ task, onTaskChange }) => {
|
|||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
case 'location':
|
||||||
|
case 'location_bay':
|
||||||
|
const locationData =
|
||||||
|
param.type.toLowerCase() === 'location' ? locations : locationsBays;
|
||||||
|
return (
|
||||||
|
<View key={param.name} style={styles.inputGroup}>
|
||||||
|
{label}
|
||||||
|
<View style={styles.pickerContainer}>
|
||||||
|
<Picker
|
||||||
|
selectedValue={value as string}
|
||||||
|
onValueChange={(itemValue: any) =>
|
||||||
|
handleParamChange(param.name, itemValue)
|
||||||
|
}
|
||||||
|
style={styles.picker}
|
||||||
|
dropdownIconColor="#00ff00"
|
||||||
|
>
|
||||||
|
{locationData.map(loc => (
|
||||||
|
<Picker.Item
|
||||||
|
key={loc.value}
|
||||||
|
label={loc.label}
|
||||||
|
value={loc.value}
|
||||||
|
color="#000000"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Picker>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
case 'payload':
|
||||||
|
return (
|
||||||
|
<View key={param.name} style={styles.inputGroup}>
|
||||||
|
{label}
|
||||||
|
<View style={styles.pickerContainer}>
|
||||||
|
<Picker
|
||||||
|
selectedValue={value as string}
|
||||||
|
onValueChange={(itemValue: any) =>
|
||||||
|
handleParamChange(param.name, itemValue)
|
||||||
|
}
|
||||||
|
style={styles.picker}
|
||||||
|
dropdownIconColor="#00ff00"
|
||||||
|
>
|
||||||
|
{payloads.map(p => (
|
||||||
|
<Picker.Item
|
||||||
|
key={p.value}
|
||||||
|
label={p.label}
|
||||||
|
value={p.value}
|
||||||
|
color="#000000"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Picker>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
case 'robotaction':
|
||||||
|
return (
|
||||||
|
<View key={param.name} style={styles.inputGroup}>
|
||||||
|
{label}
|
||||||
|
<View style={styles.pickerContainer}>
|
||||||
|
<Picker
|
||||||
|
selectedValue={(value as RobotAction)?.name}
|
||||||
|
onValueChange={(itemValue: any) => {
|
||||||
|
const selectedAction = robotActions.find(
|
||||||
|
a => a.value === itemValue,
|
||||||
|
);
|
||||||
|
if (selectedAction) {
|
||||||
|
handleParamChange(param.name, {
|
||||||
|
name: selectedAction.label,
|
||||||
|
actionId: selectedAction.actionId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={styles.picker}
|
||||||
|
dropdownIconColor="#00ff00"
|
||||||
|
>
|
||||||
|
{robotActions.map(action => (
|
||||||
|
<Picker.Item
|
||||||
|
key={action.actionId}
|
||||||
|
label={action.label}
|
||||||
|
value={action.value}
|
||||||
|
color="#000000"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Picker>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
case 'number':
|
case 'number':
|
||||||
return (
|
return (
|
||||||
<View key={param.name} style={styles.inputGroup}>
|
<View key={param.name} style={styles.inputGroup}>
|
||||||
|
@ -8,18 +8,24 @@ import React, {
|
|||||||
useCallback,
|
useCallback,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { Task } from '../types/task';
|
import { Task } from '../types/task';
|
||||||
import { AppConfig } from '../types/config';
|
|
||||||
import { getConfig } from '../services/configService';
|
|
||||||
import {
|
import {
|
||||||
getTasks as getTasksService,
|
AppConfig,
|
||||||
getTaskDetail as getTaskDetailService,
|
LocationOption,
|
||||||
} from '../services/taskService';
|
PayloadOption,
|
||||||
|
RobotActionOption,
|
||||||
|
} from '../types/config';
|
||||||
|
import { getConfig, executeTask } from '../services/configService';
|
||||||
|
|
||||||
interface TasksContextData {
|
interface TasksContextData {
|
||||||
tasks: Task[];
|
tasks: Task[];
|
||||||
|
locations: LocationOption[];
|
||||||
|
locationsBays: LocationOption[];
|
||||||
|
payloads: PayloadOption[];
|
||||||
|
robotActions: RobotActionOption[];
|
||||||
serverUrl: string | null;
|
serverUrl: string | null;
|
||||||
getTaskById: (id: string) => Task | undefined;
|
getTaskById: (id: string) => Task | undefined;
|
||||||
updateTask: (updatedTask: Task) => void;
|
updateTask: (updatedTask: Task) => void;
|
||||||
|
runTask: (id: string) => void;
|
||||||
refreshConfig: () => Promise<void>;
|
refreshConfig: () => Promise<void>;
|
||||||
isConfigLoaded: boolean;
|
isConfigLoaded: boolean;
|
||||||
fetchTaskDetail: (taskId: string) => Promise<void>;
|
fetchTaskDetail: (taskId: string) => Promise<void>;
|
||||||
@ -31,19 +37,59 @@ export const TasksProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const [tasks, setTasks] = useState<Task[]>([]);
|
const [tasks, setTasks] = useState<Task[]>([]);
|
||||||
|
const [locations, setLocations] = useState<LocationOption[]>([]);
|
||||||
|
const [locationsBays, setLocationsBays] = useState<LocationOption[]>([]);
|
||||||
|
const [payloads, setPayloads] = useState<PayloadOption[]>([]);
|
||||||
|
const [robotActions, setRobotActions] = useState<RobotActionOption[]>([]);
|
||||||
const [isConfigLoaded, setIsConfigLoaded] = useState(false);
|
const [isConfigLoaded, setIsConfigLoaded] = useState(false);
|
||||||
const [serverUrl, setServerUrl] = useState<string | null>(null);
|
const [serverUrl, setServerUrl] = useState<string | null>(null);
|
||||||
|
|
||||||
const fetchTasks = useCallback(async () => {
|
const fetchTasks = useCallback(async (baseUrl: string, endpoint: string) => {
|
||||||
if (!isConfigLoaded) return;
|
|
||||||
try {
|
try {
|
||||||
const fetchedTasks = await getTasksService({ pageNum: 1, pageSize: 100 });
|
if (baseUrl && endpoint) {
|
||||||
setTasks(fetchedTasks);
|
const fetchUrl = `${baseUrl}${endpoint}?pageNum=1&pageSize=100`;
|
||||||
|
console.log('Fetching tasks from:', fetchUrl);
|
||||||
|
const response = await fetch(fetchUrl);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseData = await response.json();
|
||||||
|
console.log('responseData', responseData);
|
||||||
|
|
||||||
|
if (responseData && responseData.code === 200) {
|
||||||
|
const fetchedTasks = responseData.data.list.map((task: any) => {
|
||||||
|
let detail = task.detail;
|
||||||
|
if (detail && typeof detail === 'string') {
|
||||||
|
try {
|
||||||
|
detail = JSON.parse(detail);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析任务详情失败 (list):', e);
|
||||||
|
detail = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...task,
|
||||||
|
name: task.label,
|
||||||
|
parameters: task.parameters || {},
|
||||||
|
detail: detail,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setTasks(fetchedTasks);
|
||||||
|
} else {
|
||||||
|
console.error('获取任务列表失败: responseData.code is not 200');
|
||||||
|
setTasks([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取任务列表失败 (context):', error);
|
console.error('获取任务列表失败:', error);
|
||||||
|
if (error instanceof Error) {
|
||||||
|
console.error('Error message:', error.message);
|
||||||
|
}
|
||||||
setTasks([]);
|
setTasks([]);
|
||||||
}
|
}
|
||||||
}, [isConfigLoaded]);
|
}, []); // Empty dependency array as it has no external dependencies from component scope
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadApp = async () => {
|
const loadApp = async () => {
|
||||||
@ -52,6 +98,9 @@ export const TasksProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
if (config) {
|
if (config) {
|
||||||
applyConfig(config);
|
applyConfig(config);
|
||||||
setIsConfigLoaded(true);
|
setIsConfigLoaded(true);
|
||||||
|
if (config.serverUrl && config.apiEndpoints) {
|
||||||
|
await fetchTasks(config.serverUrl, config.apiEndpoints.getTasks);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('没有找到配置文件,使用空数据');
|
console.log('没有找到配置文件,使用空数据');
|
||||||
setIsConfigLoaded(false);
|
setIsConfigLoaded(false);
|
||||||
@ -63,26 +112,37 @@ export const TasksProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
loadApp();
|
loadApp();
|
||||||
}, []);
|
}, [fetchTasks]);
|
||||||
|
|
||||||
useEffect(() => {
|
const [apiEndpoints, setApiEndpoints] = useState<{
|
||||||
if (isConfigLoaded) {
|
getTasks: string;
|
||||||
fetchTasks();
|
getTaskDetail: string;
|
||||||
}
|
runTask: string;
|
||||||
}, [isConfigLoaded, fetchTasks]);
|
} | null>(null);
|
||||||
|
|
||||||
const applyConfig = (config: AppConfig) => {
|
const applyConfig = (config: AppConfig) => {
|
||||||
|
setLocations(config.locations || []);
|
||||||
|
setLocationsBays(config.locationsBays || []);
|
||||||
|
setPayloads(config.payloads || []);
|
||||||
|
setRobotActions(config.robotActions || []);
|
||||||
if (config.serverUrl) {
|
if (config.serverUrl) {
|
||||||
setServerUrl(config.serverUrl);
|
setServerUrl(config.serverUrl);
|
||||||
}
|
}
|
||||||
|
if (config.apiEndpoints) {
|
||||||
|
setApiEndpoints(config.apiEndpoints);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const refreshConfig = useCallback(async () => {
|
const refreshConfig = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
|
// await clearCachedConfig(); // 通常刷新不需要清除缓存
|
||||||
const config = await getConfig();
|
const config = await getConfig();
|
||||||
if (config) {
|
if (config) {
|
||||||
applyConfig(config);
|
applyConfig(config);
|
||||||
setIsConfigLoaded(true); // 确保配置加载后再次触发任务获取
|
setIsConfigLoaded(true);
|
||||||
|
if (config.serverUrl && config.apiEndpoints) {
|
||||||
|
await fetchTasks(config.serverUrl, config.apiEndpoints.getTasks);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('刷新配置时没有找到配置文件');
|
console.log('刷新配置时没有找到配置文件');
|
||||||
setIsConfigLoaded(false);
|
setIsConfigLoaded(false);
|
||||||
@ -91,28 +151,63 @@ export const TasksProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
console.error('刷新配置失败:', error);
|
console.error('刷新配置失败:', error);
|
||||||
setIsConfigLoaded(false);
|
setIsConfigLoaded(false);
|
||||||
}
|
}
|
||||||
}, []);
|
}, [fetchTasks]);
|
||||||
|
|
||||||
const getTaskById = useCallback(
|
const getTaskById = useCallback(
|
||||||
(id: string) => {
|
(id: string) => {
|
||||||
const task = tasks.find(t => t.id === id);
|
const task = tasks.find(t => t.id === id);
|
||||||
|
if (task && !task.detail) {
|
||||||
|
// fetchTaskDetail(id); // 详情在编辑页面获取
|
||||||
|
}
|
||||||
return task;
|
return task;
|
||||||
},
|
},
|
||||||
[tasks],
|
[tasks],
|
||||||
);
|
);
|
||||||
|
|
||||||
const fetchTaskDetail = useCallback(async (taskId: string) => {
|
const fetchTaskDetail = useCallback(
|
||||||
try {
|
async (taskId: string) => {
|
||||||
const taskDetail = await getTaskDetailService(taskId);
|
try {
|
||||||
setTasks(prevTasks =>
|
if (serverUrl && apiEndpoints) {
|
||||||
prevTasks.map(task =>
|
const endpoint = apiEndpoints.getTaskDetail.replace(
|
||||||
task.id === taskId ? { ...task, detail: taskDetail.detail } : task,
|
'{taskId}',
|
||||||
),
|
taskId,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
const url = `${serverUrl}${endpoint}`;
|
||||||
console.error('获取任务详情失败 (context):', error);
|
console.log('Fetching task detail from:', url);
|
||||||
}
|
const response = await fetch(url);
|
||||||
}, []);
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseData = await response.json();
|
||||||
|
console.log('task detail responseData', responseData);
|
||||||
|
|
||||||
|
if (responseData && responseData.code === 200) {
|
||||||
|
let taskDetail = responseData.data.detail;
|
||||||
|
if (taskDetail && typeof taskDetail === 'string') {
|
||||||
|
try {
|
||||||
|
taskDetail = JSON.parse(taskDetail);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析任务详情失败 (detail):', e);
|
||||||
|
taskDetail = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTasks(prevTasks =>
|
||||||
|
prevTasks.map(task =>
|
||||||
|
task.id === taskId ? { ...task, detail: taskDetail } : task,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.error('获取任务详情失败: responseData.code is not 200');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取任务详情失败:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[serverUrl, apiEndpoints],
|
||||||
|
);
|
||||||
|
|
||||||
const updateTask = useCallback((updatedTask: Task) => {
|
const updateTask = useCallback((updatedTask: Task) => {
|
||||||
setTasks(prevTasks =>
|
setTasks(prevTasks =>
|
||||||
@ -120,21 +215,72 @@ export const TasksProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const runTask = useCallback(
|
||||||
|
async (id: string) => {
|
||||||
|
const task = tasks.find(t => t.id === id);
|
||||||
|
if (!task || !serverUrl || !apiEndpoints) return;
|
||||||
|
|
||||||
|
setTasks(prevTasks =>
|
||||||
|
prevTasks.map(t => (t.id === id ? { ...t, status: 1 } : t)),
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parameters = Object.entries(task.parameters).reduce(
|
||||||
|
(acc, [key, param]) => {
|
||||||
|
if (param) {
|
||||||
|
acc[key] = param.value;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as { [key: string]: any },
|
||||||
|
);
|
||||||
|
|
||||||
|
const endpoint = apiEndpoints.runTask.replace('{taskId}', task.id);
|
||||||
|
await executeTask(serverUrl, endpoint, {
|
||||||
|
name: task.name,
|
||||||
|
parameters: parameters,
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setTasks(prevTasks =>
|
||||||
|
prevTasks.map(t => (t.id === id ? { ...t, status: 2 } : t)),
|
||||||
|
);
|
||||||
|
}, 5000);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('任务执行失败:', error);
|
||||||
|
setTasks(prevTasks =>
|
||||||
|
prevTasks.map(t => (t.id === id ? { ...t, status: 3 } : t)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[tasks, serverUrl, apiEndpoints],
|
||||||
|
);
|
||||||
|
|
||||||
const contextValue = useMemo(
|
const contextValue = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
tasks,
|
tasks,
|
||||||
|
locations,
|
||||||
|
locationsBays,
|
||||||
|
payloads,
|
||||||
|
robotActions,
|
||||||
serverUrl,
|
serverUrl,
|
||||||
getTaskById,
|
getTaskById,
|
||||||
updateTask,
|
updateTask,
|
||||||
|
runTask,
|
||||||
refreshConfig,
|
refreshConfig,
|
||||||
isConfigLoaded,
|
isConfigLoaded,
|
||||||
fetchTaskDetail,
|
fetchTaskDetail,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
tasks,
|
tasks,
|
||||||
|
locations,
|
||||||
|
locationsBays,
|
||||||
|
payloads,
|
||||||
|
robotActions,
|
||||||
serverUrl,
|
serverUrl,
|
||||||
getTaskById,
|
getTaskById,
|
||||||
updateTask,
|
updateTask,
|
||||||
|
runTask,
|
||||||
refreshConfig,
|
refreshConfig,
|
||||||
isConfigLoaded,
|
isConfigLoaded,
|
||||||
fetchTaskDetail,
|
fetchTaskDetail,
|
||||||
|
@ -51,8 +51,8 @@ export default function AppNavigator() {
|
|||||||
|
|
||||||
if (route.name === '主页') {
|
if (route.name === '主页') {
|
||||||
iconName = 'home';
|
iconName = 'home';
|
||||||
} else if (route.name === '任务列表') {
|
} else if (route.name === '运行') {
|
||||||
iconName = 'view-list';
|
iconName = 'play-arrow';
|
||||||
} else if (route.name === '编辑') {
|
} else if (route.name === '编辑') {
|
||||||
iconName = 'edit';
|
iconName = 'edit';
|
||||||
} else if (route.name === '设置') {
|
} else if (route.name === '设置') {
|
||||||
@ -73,7 +73,7 @@ export default function AppNavigator() {
|
|||||||
component={HomeStackNavigator}
|
component={HomeStackNavigator}
|
||||||
options={{ headerShown: false }}
|
options={{ headerShown: false }}
|
||||||
/>
|
/>
|
||||||
<Tab.Screen name="任务列表" component={RunScreen} />
|
<Tab.Screen name="运行" component={RunScreen} />
|
||||||
<Tab.Screen name="编辑" component={EditScreen} />
|
<Tab.Screen name="编辑" component={EditScreen} />
|
||||||
<Tab.Screen name="设置" component={SettingsScreen} />
|
<Tab.Screen name="设置" component={SettingsScreen} />
|
||||||
</Tab.Navigator>
|
</Tab.Navigator>
|
||||||
|
@ -4,8 +4,7 @@ import { StyleSheet, Text, View } from 'react-native';
|
|||||||
export default function RunScreen() {
|
export default function RunScreen() {
|
||||||
return (
|
return (
|
||||||
<View style={styles.screenContainer}>
|
<View style={styles.screenContainer}>
|
||||||
<Text style={styles.text}>任务运行状态列表</Text>
|
<Text>运行!</Text>
|
||||||
<Text style={styles.subText}>此功能正在开发中...</Text>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -15,16 +14,5 @@ const styles = StyleSheet.create({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
backgroundColor: '#121212',
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
fontSize: 22,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#ffffff',
|
|
||||||
marginBottom: 10,
|
|
||||||
},
|
|
||||||
subText: {
|
|
||||||
fontSize: 16,
|
|
||||||
color: '#b0b0b0',
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -5,15 +5,13 @@ import {
|
|||||||
Alert,
|
Alert,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
Text,
|
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { useRoute, useNavigation } from '@react-navigation/native';
|
import { useRoute, useNavigation } from '@react-navigation/native';
|
||||||
import { RouteProp } from '@react-navigation/native';
|
import { RouteProp } from '@react-navigation/native';
|
||||||
import { useTasks } from '../context/TasksContext';
|
import { useTasks } from '../context/TasksContext';
|
||||||
import TaskForm from '../components/TaskForm';
|
import TaskForm from '../components/TaskForm';
|
||||||
import BottomActionBar from '../components/BottomActionBar';
|
import BottomActionBar from '../components/BottomActionBar';
|
||||||
import { Task, RunTaskRequest, RunTaskApiResponse } from '../types/task';
|
import { Task } from '../types/task';
|
||||||
import { runTask as runTaskService } from '../services/taskService';
|
|
||||||
|
|
||||||
type RootStackParamList = {
|
type RootStackParamList = {
|
||||||
TaskEdit: { taskId: string };
|
TaskEdit: { taskId: string };
|
||||||
@ -26,22 +24,19 @@ export default function TaskEditScreen() {
|
|||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
const { taskId } = route.params;
|
const { taskId } = route.params;
|
||||||
|
|
||||||
const { getTaskById, updateTask, tasks, fetchTaskDetail } = useTasks();
|
const { getTaskById, updateTask, runTask, tasks, fetchTaskDetail } =
|
||||||
|
useTasks();
|
||||||
|
|
||||||
const [task, setTask] = useState<Task | null>(null);
|
const [task, setTask] = useState<Task | null>(null);
|
||||||
const [originalTask, setOriginalTask] = useState<Task | null>(null);
|
const [originalTask, setOriginalTask] = useState<Task | null>(null);
|
||||||
const [isModified, setIsModified] = useState(false);
|
const [isModified, setIsModified] = useState(false);
|
||||||
const [isRunLoading, setIsRunLoading] = useState(false);
|
|
||||||
const [runResponse, setRunResponse] = useState<RunTaskApiResponse | null>(
|
|
||||||
null,
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadTask = async () => {
|
const loadTask = async () => {
|
||||||
let taskData = getTaskById(taskId);
|
let taskData = getTaskById(taskId);
|
||||||
debugger;
|
|
||||||
if (taskData && !taskData.detail) {
|
if (taskData && !taskData.detail) {
|
||||||
await fetchTaskDetail(taskId);
|
await fetchTaskDetail(taskId);
|
||||||
|
// Re-fetch task data after details are loaded
|
||||||
taskData = getTaskById(taskId);
|
taskData = getTaskById(taskId);
|
||||||
}
|
}
|
||||||
if (taskData) {
|
if (taskData) {
|
||||||
@ -68,40 +63,10 @@ export default function TaskEditScreen() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRun = async () => {
|
const handleRun = () => {
|
||||||
if (!task || !task.detail || !task.detail.inputParams) {
|
if (task) {
|
||||||
Alert.alert('错误', '任务数据不完整,无法运行。');
|
runTask(task.id);
|
||||||
return;
|
navigation.goBack();
|
||||||
}
|
|
||||||
|
|
||||||
setIsRunLoading(true);
|
|
||||||
setRunResponse(null);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const params = task.detail.inputParams.map(p => ({
|
|
||||||
name: p.name,
|
|
||||||
type: p.type,
|
|
||||||
label: p.label,
|
|
||||||
required: p.required,
|
|
||||||
defaultValue: p.defaultValue,
|
|
||||||
remark: p.remark,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const request: RunTaskRequest = {
|
|
||||||
taskId: task.id,
|
|
||||||
params: params,
|
|
||||||
source_type: 1,
|
|
||||||
source_system: 'SYSTEM',
|
|
||||||
source_device: '666',
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await runTaskService(request);
|
|
||||||
setRunResponse(result);
|
|
||||||
Alert.alert('成功', `任务已启动, 记录ID: ${result.data.taskRecordId}`);
|
|
||||||
} catch (error: any) {
|
|
||||||
Alert.alert('运行失败', error.message || '发生未知错误');
|
|
||||||
} finally {
|
|
||||||
setIsRunLoading(false);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -110,6 +75,11 @@ export default function TaskEditScreen() {
|
|||||||
setIsModified(false);
|
setIsModified(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleRestore = () => {
|
||||||
|
setTask(originalTask);
|
||||||
|
setIsModified(false);
|
||||||
|
};
|
||||||
|
|
||||||
if (!task) {
|
if (!task) {
|
||||||
return (
|
return (
|
||||||
<View style={styles.loadingContainer}>
|
<View style={styles.loadingContainer}>
|
||||||
@ -122,22 +92,14 @@ export default function TaskEditScreen() {
|
|||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<ScrollView contentContainerStyle={styles.scrollContainer}>
|
<ScrollView contentContainerStyle={styles.scrollContainer}>
|
||||||
<TaskForm task={task} onTaskChange={handleTaskChange} />
|
<TaskForm task={task} onTaskChange={handleTaskChange} />
|
||||||
{runResponse && (
|
|
||||||
<View style={styles.responseContainer}>
|
|
||||||
<Text style={styles.responseTitle}>运行结果:</Text>
|
|
||||||
<Text style={styles.responseText}>
|
|
||||||
{JSON.stringify(runResponse, null, 2)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
<BottomActionBar
|
<BottomActionBar
|
||||||
onRun={handleRun}
|
onRun={handleRun}
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
onUndo={handleUndo}
|
onUndo={handleUndo}
|
||||||
|
onRestore={handleRestore}
|
||||||
onBack={() => navigation.goBack()}
|
onBack={() => navigation.goBack()}
|
||||||
isSaveDisabled={!isModified}
|
isSaveDisabled={!isModified}
|
||||||
isRunLoading={isRunLoading}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
@ -146,7 +108,7 @@ export default function TaskEditScreen() {
|
|||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: '#1a1a1a',
|
backgroundColor: '#1a1a1a', // 深色背景
|
||||||
},
|
},
|
||||||
loadingContainer: {
|
loadingContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@ -156,23 +118,5 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
scrollContainer: {
|
scrollContainer: {
|
||||||
padding: 16,
|
padding: 16,
|
||||||
paddingBottom: 80,
|
|
||||||
},
|
|
||||||
responseContainer: {
|
|
||||||
marginTop: 20,
|
|
||||||
padding: 15,
|
|
||||||
backgroundColor: '#2c2c2c',
|
|
||||||
borderRadius: 8,
|
|
||||||
},
|
|
||||||
responseTitle: {
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#ffffff',
|
|
||||||
marginBottom: 10,
|
|
||||||
},
|
|
||||||
responseText: {
|
|
||||||
fontSize: 14,
|
|
||||||
color: '#e0e0e0',
|
|
||||||
fontFamily: 'monospace',
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -17,8 +17,6 @@ type TaskListNavigationProp = StackNavigationProp<
|
|||||||
|
|
||||||
export default function TaskListScreen() {
|
export default function TaskListScreen() {
|
||||||
const { tasks } = useTasks();
|
const { tasks } = useTasks();
|
||||||
console.log('tasks', tasks);
|
|
||||||
|
|
||||||
const navigation = useNavigation<TaskListNavigationProp>();
|
const navigation = useNavigation<TaskListNavigationProp>();
|
||||||
|
|
||||||
const handlePressTask = (id: string) => {
|
const handlePressTask = (id: string) => {
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
import axios from 'axios';
|
|
||||||
import { getConfig, getSettings } from './configService';
|
|
||||||
|
|
||||||
// 创建 axios 实例
|
|
||||||
const api = axios.create({
|
|
||||||
timeout: 10000, // 请求超时时间
|
|
||||||
});
|
|
||||||
|
|
||||||
// 请求拦截器
|
|
||||||
api.interceptors.request.use(
|
|
||||||
async config => {
|
|
||||||
const appConfig = await getConfig();
|
|
||||||
const settings = await getSettings();
|
|
||||||
const serverUrl = settings.serverUrl || appConfig?.serverUrl;
|
|
||||||
|
|
||||||
if (!serverUrl) {
|
|
||||||
const error = new Error('服务器地址未配置');
|
|
||||||
console.error(error.message);
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
config.baseURL = serverUrl.replace(/\/$/, '');
|
|
||||||
// 设置通用请求头
|
|
||||||
config.headers = {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
...appConfig?.headers,
|
|
||||||
...config.headers,
|
|
||||||
} as any;
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`[Request] ${config.method?.toUpperCase()} ${config.baseURL}${
|
|
||||||
config.url
|
|
||||||
}`,
|
|
||||||
{ params: config.params, data: config.data },
|
|
||||||
);
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
console.error('[Request Error]', error);
|
|
||||||
return Promise.reject(error);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// 响应拦截器
|
|
||||||
api.interceptors.response.use(
|
|
||||||
response => {
|
|
||||||
// 统一处理API响应
|
|
||||||
if (response.data && response.data.code && response.data.code !== 200) {
|
|
||||||
throw new Error(`API 错误: ${response.data.message}`);
|
|
||||||
}
|
|
||||||
// 优先处理嵌套的 data 字段
|
|
||||||
if (response.data && typeof response.data.data !== 'undefined') {
|
|
||||||
// 如果 data 字段中有 list,则返回 list
|
|
||||||
if (Array.isArray(response.data.data.list)) {
|
|
||||||
return response.data.data.list;
|
|
||||||
}
|
|
||||||
// 否则返回 data 字段本身
|
|
||||||
return response.data.data;
|
|
||||||
}
|
|
||||||
// 如果没有嵌套的 data 字段,直接返回整个响应数据
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
if (error.response) {
|
|
||||||
// 请求已发出,但服务器响应的状态码不在 2xx 范围
|
|
||||||
console.error(
|
|
||||||
`[Response Error] Status: ${error.response.status}`,
|
|
||||||
error.response.data,
|
|
||||||
);
|
|
||||||
const httpError = new Error(
|
|
||||||
`HTTP ${error.response.status}: ${
|
|
||||||
error.response.statusText || 'Unknown Error'
|
|
||||||
}`,
|
|
||||||
);
|
|
||||||
return Promise.reject(httpError);
|
|
||||||
} else if (error.request) {
|
|
||||||
// 请求已发出,但没有收到响应
|
|
||||||
console.error('[Network Error]', error.request);
|
|
||||||
return Promise.reject(new Error('网络错误,请检查您的连接'));
|
|
||||||
} else {
|
|
||||||
// 在设置请求时触发了一个错误
|
|
||||||
console.error('[Request Setup Error]', error.message);
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export default api;
|
|
@ -1,54 +0,0 @@
|
|||||||
import { getConfig } from './configService';
|
|
||||||
import { RunTaskRequest, RunTaskApiResponse, Task } from '../types/task';
|
|
||||||
import api from './api';
|
|
||||||
|
|
||||||
// 获取任务列表
|
|
||||||
export const getTasks = async (params: {
|
|
||||||
pageNum: number;
|
|
||||||
pageSize: number;
|
|
||||||
}): Promise<Task[]> => {
|
|
||||||
try {
|
|
||||||
const config = await getConfig();
|
|
||||||
const endpoint = config?.apiEndpoints?.getTasks;
|
|
||||||
if (!endpoint) throw new Error('获取任务列表的API端点未配置');
|
|
||||||
|
|
||||||
const response = await api.get(endpoint, { params });
|
|
||||||
return response as unknown as Task[];
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取任务列表失败 (service):', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 获取任务详情
|
|
||||||
export const getTaskDetail = async (taskId: string): Promise<Task> => {
|
|
||||||
try {
|
|
||||||
const config = await getConfig();
|
|
||||||
let endpoint = config?.apiEndpoints?.getTaskDetail;
|
|
||||||
if (!endpoint) throw new Error('获取任务详情的API端点未配置');
|
|
||||||
|
|
||||||
endpoint = endpoint.replace('{taskId}', taskId);
|
|
||||||
const response = await api.get(endpoint);
|
|
||||||
return response as unknown as Task;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`获取任务详情失败 (ID: ${taskId}):`, error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 运行任务
|
|
||||||
export const runTask = async (
|
|
||||||
taskData: RunTaskRequest,
|
|
||||||
): Promise<RunTaskApiResponse> => {
|
|
||||||
try {
|
|
||||||
const config = await getConfig();
|
|
||||||
const endpoint = config?.apiEndpoints?.runTask;
|
|
||||||
if (!endpoint) throw new Error('运行任务的API端点未配置');
|
|
||||||
|
|
||||||
const response = await api.post(endpoint, taskData);
|
|
||||||
return response as unknown as RunTaskApiResponse;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('运行任务失败:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,15 +1,35 @@
|
|||||||
|
// 配置文件中的位置选项
|
||||||
|
export interface LocationOption {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置文件中的载荷选项
|
||||||
|
export interface PayloadOption {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置文件中的机器人动作选项
|
||||||
|
export interface RobotActionOption {
|
||||||
|
label: string;
|
||||||
|
value: string; // 动作的名称,作为 Picker 的 value
|
||||||
|
actionId: string;
|
||||||
|
}
|
||||||
|
|
||||||
// 完整的配置文件结构
|
// 完整的配置文件结构
|
||||||
export interface AppConfig {
|
export interface AppConfig {
|
||||||
version: string;
|
version: string;
|
||||||
|
locations: LocationOption[];
|
||||||
|
locationsBays: LocationOption[];
|
||||||
|
payloads: PayloadOption[];
|
||||||
|
robotActions: RobotActionOption[];
|
||||||
serverUrl?: string; // 服务器地址
|
serverUrl?: string; // 服务器地址
|
||||||
apiEndpoints?: {
|
apiEndpoints?: {
|
||||||
getTasks: string;
|
getTasks: string;
|
||||||
getTaskDetail: string;
|
getTaskDetail: string;
|
||||||
runTask: string;
|
runTask: string;
|
||||||
};
|
};
|
||||||
headers?: {
|
|
||||||
[key: string]: string | number;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置存储接口
|
// 设置存储接口
|
||||||
|
@ -63,33 +63,3 @@ export interface InputParam {
|
|||||||
max?: number;
|
max?: number;
|
||||||
step?: number;
|
step?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 运行任务请求体
|
|
||||||
export interface RunTaskRequest {
|
|
||||||
taskId: string;
|
|
||||||
params: Array<{
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
label: string;
|
|
||||||
required: boolean;
|
|
||||||
defaultValue: string;
|
|
||||||
remark: string;
|
|
||||||
}>;
|
|
||||||
source_type: number;
|
|
||||||
source_system: string;
|
|
||||||
source_device: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 运行任务响应数据
|
|
||||||
export interface RunTaskResponseData {
|
|
||||||
taskRecordId: string;
|
|
||||||
status: number;
|
|
||||||
createTime: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 运行任务API响应
|
|
||||||
export interface RunTaskApiResponse {
|
|
||||||
code: number;
|
|
||||||
message: string;
|
|
||||||
data: RunTaskResponseData;
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user