feat: 添加任务运行功能,更新相关组件以支持任务执行和状态反馈
This commit is contained in:
parent
ce11581196
commit
f9564796ab
13
App.tsx
13
App.tsx
@ -1,11 +1,22 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import AppNavigator from './src/navigation/AppNavigator';
|
||||
|
||||
import { TasksProvider } from './src/context/TasksContext';
|
||||
import { ThemeProvider } from '@rneui/themed';
|
||||
import { clearCachedConfig } from './src/services/configService';
|
||||
|
||||
export default function App() {
|
||||
useEffect(() => {
|
||||
const clearCacheOnStart = async () => {
|
||||
console.log('正在清除缓存的配置文件...');
|
||||
await clearCachedConfig();
|
||||
console.log('缓存已清除,将使用最新的 config.json。');
|
||||
};
|
||||
|
||||
clearCacheOnStart();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ThemeProvider>
|
||||
<TasksProvider>
|
||||
|
@ -40,6 +40,6 @@
|
||||
"apiEndpoints": {
|
||||
"getTasks": "/api/vwed-task/list",
|
||||
"getTaskDetail": "/api/vwed-task/{taskId}",
|
||||
"runTask": "/api/vwed-task/execute/{taskId}"
|
||||
"runTask": "/api/vwed-task-edit/run"
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,11 @@
|
||||
import React from 'react';
|
||||
import { View, StyleSheet, TouchableOpacity, Text } from 'react-native';
|
||||
import {
|
||||
View,
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
Text,
|
||||
ActivityIndicator,
|
||||
} from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
|
||||
interface BottomActionBarProps {
|
||||
@ -8,6 +14,7 @@ interface BottomActionBarProps {
|
||||
onUndo: () => void;
|
||||
onBack: () => void;
|
||||
isSaveDisabled?: boolean;
|
||||
isRunLoading?: boolean;
|
||||
}
|
||||
|
||||
const BottomActionBar: React.FC<BottomActionBarProps> = ({
|
||||
@ -16,6 +23,7 @@ const BottomActionBar: React.FC<BottomActionBarProps> = ({
|
||||
onUndo,
|
||||
onBack,
|
||||
isSaveDisabled = true,
|
||||
isRunLoading = false,
|
||||
}) => {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
@ -48,10 +56,21 @@ const BottomActionBar: React.FC<BottomActionBarProps> = ({
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onPress={onRun}
|
||||
style={[styles.button, styles.runButton]}
|
||||
disabled={isRunLoading}
|
||||
style={[
|
||||
styles.button,
|
||||
styles.runButton,
|
||||
isRunLoading && styles.disabledButton,
|
||||
]}
|
||||
>
|
||||
<Icon name="play" size={24} color="#FFFFFF" />
|
||||
<Text style={[styles.buttonText, styles.runButtonText]}>运行</Text>
|
||||
{isRunLoading ? (
|
||||
<ActivityIndicator size="small" color="#FFFFFF" />
|
||||
) : (
|
||||
<>
|
||||
<Icon name="play" size={24} color="#FFFFFF" />
|
||||
<Text style={[styles.buttonText, styles.runButtonText]}>运行</Text>
|
||||
</>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
|
@ -51,8 +51,8 @@ export default function AppNavigator() {
|
||||
|
||||
if (route.name === '主页') {
|
||||
iconName = 'home';
|
||||
} else if (route.name === '运行') {
|
||||
iconName = 'play-arrow';
|
||||
} else if (route.name === '任务列表') {
|
||||
iconName = 'view-list';
|
||||
} else if (route.name === '编辑') {
|
||||
iconName = 'edit';
|
||||
} else if (route.name === '设置') {
|
||||
@ -73,7 +73,7 @@ export default function AppNavigator() {
|
||||
component={HomeStackNavigator}
|
||||
options={{ headerShown: false }}
|
||||
/>
|
||||
<Tab.Screen name="运行" component={RunScreen} />
|
||||
<Tab.Screen name="任务列表" component={RunScreen} />
|
||||
<Tab.Screen name="编辑" component={EditScreen} />
|
||||
<Tab.Screen name="设置" component={SettingsScreen} />
|
||||
</Tab.Navigator>
|
||||
|
@ -4,7 +4,8 @@ import { StyleSheet, Text, View } from 'react-native';
|
||||
export default function RunScreen() {
|
||||
return (
|
||||
<View style={styles.screenContainer}>
|
||||
<Text>运行!</Text>
|
||||
<Text style={styles.text}>任务运行状态列表</Text>
|
||||
<Text style={styles.subText}>此功能正在开发中...</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@ -14,5 +15,16 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#121212',
|
||||
},
|
||||
text: {
|
||||
fontSize: 22,
|
||||
fontWeight: 'bold',
|
||||
color: '#ffffff',
|
||||
marginBottom: 10,
|
||||
},
|
||||
subText: {
|
||||
fontSize: 16,
|
||||
color: '#b0b0b0',
|
||||
},
|
||||
});
|
||||
|
@ -5,13 +5,15 @@ import {
|
||||
Alert,
|
||||
ScrollView,
|
||||
ActivityIndicator,
|
||||
Text,
|
||||
} 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 { Task, RunTaskRequest, RunTaskApiResponse } from '../types/task';
|
||||
import { runTask as runTaskService } from '../services/taskService';
|
||||
|
||||
type RootStackParamList = {
|
||||
TaskEdit: { taskId: string };
|
||||
@ -24,19 +26,21 @@ export default function TaskEditScreen() {
|
||||
const navigation = useNavigation();
|
||||
const { taskId } = route.params;
|
||||
|
||||
const { getTaskById, updateTask, runTask, tasks, fetchTaskDetail } =
|
||||
useTasks();
|
||||
const { getTaskById, updateTask, tasks, fetchTaskDetail } = useTasks();
|
||||
|
||||
const [task, setTask] = useState<Task | null>(null);
|
||||
const [originalTask, setOriginalTask] = useState<Task | null>(null);
|
||||
const [isModified, setIsModified] = useState(false);
|
||||
const [isRunLoading, setIsRunLoading] = useState(false);
|
||||
const [runResponse, setRunResponse] = useState<RunTaskApiResponse | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const loadTask = async () => {
|
||||
let taskData = getTaskById(taskId);
|
||||
if (taskData && !taskData.detail) {
|
||||
await fetchTaskDetail(taskId);
|
||||
// Re-fetch task data after details are loaded
|
||||
taskData = getTaskById(taskId);
|
||||
}
|
||||
if (taskData) {
|
||||
@ -63,10 +67,40 @@ export default function TaskEditScreen() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleRun = () => {
|
||||
if (task) {
|
||||
runTask(task.id);
|
||||
navigation.goBack();
|
||||
const handleRun = async () => {
|
||||
if (!task || !task.detail || !task.detail.inputParams) {
|
||||
Alert.alert('错误', '任务数据不完整,无法运行。');
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
@ -75,11 +109,6 @@ export default function TaskEditScreen() {
|
||||
setIsModified(false);
|
||||
};
|
||||
|
||||
const handleRestore = () => {
|
||||
setTask(originalTask);
|
||||
setIsModified(false);
|
||||
};
|
||||
|
||||
if (!task) {
|
||||
return (
|
||||
<View style={styles.loadingContainer}>
|
||||
@ -92,14 +121,22 @@ export default function TaskEditScreen() {
|
||||
<View style={styles.container}>
|
||||
<ScrollView contentContainerStyle={styles.scrollContainer}>
|
||||
<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>
|
||||
<BottomActionBar
|
||||
onRun={handleRun}
|
||||
onSave={handleSave}
|
||||
onUndo={handleUndo}
|
||||
onRestore={handleRestore}
|
||||
onBack={() => navigation.goBack()}
|
||||
isSaveDisabled={!isModified}
|
||||
isRunLoading={isRunLoading}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
@ -108,7 +145,7 @@ export default function TaskEditScreen() {
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#1a1a1a', // 深色背景
|
||||
backgroundColor: '#1a1a1a',
|
||||
},
|
||||
loadingContainer: {
|
||||
flex: 1,
|
||||
@ -118,5 +155,23 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
scrollContainer: {
|
||||
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',
|
||||
},
|
||||
});
|
||||
|
126
src/services/taskService.ts
Normal file
126
src/services/taskService.ts
Normal file
@ -0,0 +1,126 @@
|
||||
import { getSettings, getConfig } from './configService';
|
||||
import { RunTaskRequest, RunTaskApiResponse, Task } from '../types/task';
|
||||
|
||||
// 获取任务列表
|
||||
export const getTasks = async (): Promise<Task[]> => {
|
||||
try {
|
||||
const config = await getConfig();
|
||||
const endpoint = config?.apiEndpoints?.getTasks;
|
||||
|
||||
if (!endpoint) {
|
||||
throw new Error('获取任务列表的API端点未配置');
|
||||
}
|
||||
|
||||
const settings = await getSettings();
|
||||
const serverUrl = settings.serverUrl || config?.serverUrl;
|
||||
|
||||
if (!serverUrl) {
|
||||
throw new Error('服务器地址未配置');
|
||||
}
|
||||
|
||||
const url = `${serverUrl.replace(/\/$/, '')}${endpoint}`;
|
||||
console.log('获取任务列表:', url);
|
||||
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
if (result.code !== 200) {
|
||||
throw new Error(`API 错误: ${result.message}`);
|
||||
}
|
||||
|
||||
return result.data;
|
||||
} catch (error) {
|
||||
console.error('获取任务列表失败:', 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 settings = await getSettings();
|
||||
const serverUrl = settings.serverUrl || config?.serverUrl;
|
||||
|
||||
if (!serverUrl) {
|
||||
throw new Error('服务器地址未配置');
|
||||
}
|
||||
|
||||
const url = `${serverUrl.replace(/\/$/, '')}${endpoint}`;
|
||||
console.log('获取任务详情:', url);
|
||||
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
if (result.code !== 200) {
|
||||
throw new Error(`API 错误: ${result.message}`);
|
||||
}
|
||||
|
||||
return result.data;
|
||||
} 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 settings = await getSettings();
|
||||
const serverUrl = settings.serverUrl || config?.serverUrl;
|
||||
|
||||
if (!serverUrl) {
|
||||
throw new Error('服务器地址未配置');
|
||||
}
|
||||
|
||||
const url = `${serverUrl.replace(/\/$/, '')}${endpoint}`;
|
||||
console.log('运行任务请求:', url, JSON.stringify(taskData, null, 2));
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(taskData),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
||||
}
|
||||
|
||||
const result: RunTaskApiResponse = await response.json();
|
||||
console.log('运行任务响应:', result);
|
||||
|
||||
if (result.code !== 200) {
|
||||
throw new Error(`API 错误: ${result.message}`);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('运行任务失败:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
@ -63,3 +63,33 @@ export interface InputParam {
|
||||
max?: 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