feat: 添加任务管理API支持,更新任务列表和运行屏幕以实现任务的创建、终止和状态管理功能
This commit is contained in:
parent
ece6728e94
commit
97164ff220
12
config.json
12
config.json
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"serverUrl": "http://192.168.189.206:8000",
|
"serverUrl": "http://192.168.189.206:8000",
|
||||||
|
"taskServerUrl": "http://192.168.189.206:8080/jeecg-boot",
|
||||||
"headers": {
|
"headers": {
|
||||||
"x-access-token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NTIzMzQ5MjEsInVzZXJuYW1lIjoiYWRtaW4ifQ.dKvKIJOU5FxFeFNI3ucJTPLhHmUFZkv8HA2S_VO6klY",
|
"x-access-token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NTIzMzQ5MjEsInVzZXJuYW1lIjoiYWRtaW4ifQ.dKvKIJOU5FxFeFNI3ucJTPLhHmUFZkv8HA2S_VO6klY",
|
||||||
"x-tenant-id": 1000
|
"x-tenant-id": 1000
|
||||||
@ -9,5 +10,16 @@
|
|||||||
"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-edit/run"
|
||||||
|
},
|
||||||
|
"taskApiEndpoints": {
|
||||||
|
"getTaskList": "/task",
|
||||||
|
"createTask": "/task",
|
||||||
|
"getTaskDetail": "/task/{id}",
|
||||||
|
"getTaskProgress": "/task/proce",
|
||||||
|
"adjustTaskPriority": "/task/{id}/priority",
|
||||||
|
"pauseTask": "/task/{id}/paused",
|
||||||
|
"continueTask": "/task/{id}/continue",
|
||||||
|
"terminateTask": "/task/{id}/terminated",
|
||||||
|
"exportTask": "/task/exportXls"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,57 +7,27 @@ interface TaskCardProps {
|
|||||||
onPress: (id: string) => void;
|
onPress: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusMap: { [key: number]: { text: string; color: string } } = {
|
// 主页任务卡片不需要显示状态,因为都是未运行的任务模板
|
||||||
0: { text: '待机', color: '#9E9E9E' }, // Grey
|
|
||||||
1: { text: '运行中', color: '#2196F3' }, // Blue
|
|
||||||
2: { text: '完成', color: '#4CAF50' }, // Green
|
|
||||||
3: { text: '失败', color: '#F44336' }, // Red
|
|
||||||
};
|
|
||||||
|
|
||||||
const TaskCard: React.FC<TaskCardProps> = ({ task, onPress }) => {
|
const TaskCard: React.FC<TaskCardProps> = ({ task, onPress }) => {
|
||||||
const statusInfo = statusMap[task.status] || statusMap[0];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity onPress={() => onPress(task.id)} style={styles.container}>
|
<TouchableOpacity onPress={() => onPress(task.id)} style={styles.container}>
|
||||||
<View style={styles.indicatorContainer}>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
styles.statusIndicator,
|
|
||||||
{ backgroundColor: statusInfo.color },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View style={styles.contentContainer}>
|
<View style={styles.contentContainer}>
|
||||||
<Text style={styles.title}>{task.label}</Text>
|
<Text style={styles.title}>{task.label}</Text>
|
||||||
<Text style={styles.description} numberOfLines={1}>
|
<Text style={styles.description} numberOfLines={2}>
|
||||||
{task.description || '暂无描述'}
|
{task.description || '暂无描述'}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.statusContainer}>
|
|
||||||
<Text style={[styles.statusText, { color: statusInfo.color }]}>
|
|
||||||
{statusInfo.text}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
backgroundColor: '#1a1a1a',
|
backgroundColor: '#1a1a1a',
|
||||||
paddingVertical: 16,
|
paddingVertical: 16,
|
||||||
paddingHorizontal: 16,
|
paddingHorizontal: 16,
|
||||||
},
|
},
|
||||||
indicatorContainer: {
|
|
||||||
marginRight: 16,
|
|
||||||
},
|
|
||||||
statusIndicator: {
|
|
||||||
width: 12,
|
|
||||||
height: 12,
|
|
||||||
borderRadius: 6,
|
|
||||||
},
|
|
||||||
contentContainer: {
|
contentContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
@ -65,18 +35,12 @@ const styles = StyleSheet.create({
|
|||||||
color: '#FFFFFF',
|
color: '#FFFFFF',
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
marginBottom: 4,
|
marginBottom: 8,
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
color: '#999999',
|
color: '#999999',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
},
|
lineHeight: 20,
|
||||||
statusContainer: {
|
|
||||||
marginLeft: 16,
|
|
||||||
},
|
|
||||||
statusText: {
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,30 +1,684 @@
|
|||||||
import React from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import { StyleSheet, Text, View } from 'react-native';
|
import {
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
View,
|
||||||
|
FlatList,
|
||||||
|
TouchableOpacity,
|
||||||
|
ActivityIndicator,
|
||||||
|
Alert,
|
||||||
|
TextInput,
|
||||||
|
} from 'react-native';
|
||||||
|
import { Picker } from '@react-native-picker/picker';
|
||||||
|
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
||||||
|
import { TaskStatus } from '../types/task';
|
||||||
|
import { getTaskRunList, terminateTask } from '../services/taskService';
|
||||||
|
|
||||||
|
// 任务状态选项
|
||||||
|
const statusOptions = [
|
||||||
|
{ label: '全部', value: undefined },
|
||||||
|
{ label: '待分配', value: TaskStatus.WAITING },
|
||||||
|
{ label: '执行中', value: TaskStatus.RUNNING },
|
||||||
|
{ label: '已暂停', value: TaskStatus.PAUSED },
|
||||||
|
{ label: '已完成', value: TaskStatus.COMPLETED },
|
||||||
|
{ label: '已失败', value: TaskStatus.FAILED },
|
||||||
|
{ label: '已终止', value: TaskStatus.TERMINATED },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 状态显示配置
|
||||||
|
const statusDisplayMap: { [key: number]: { text: string; color: string } } = {
|
||||||
|
[TaskStatus.WAITING]: { text: '待分配', color: '#9E9E9E' },
|
||||||
|
[TaskStatus.RUNNING]: { text: '执行中', color: '#2196F3' },
|
||||||
|
[TaskStatus.PAUSED]: { text: '已暂停', color: '#FF9800' },
|
||||||
|
[TaskStatus.COMPLETED]: { text: '已完成', color: '#4CAF50' },
|
||||||
|
[TaskStatus.FAILED]: { text: '已失败', color: '#F44336' },
|
||||||
|
[TaskStatus.TERMINATED]: { text: '已终止', color: '#9C27B0' },
|
||||||
|
};
|
||||||
|
|
||||||
|
interface TaskRunItem {
|
||||||
|
id: string;
|
||||||
|
parentId: string;
|
||||||
|
vwedTaskId: string;
|
||||||
|
vwedTaskParentId: string;
|
||||||
|
sceneId: string;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
priority: number;
|
||||||
|
isPeriodic: number;
|
||||||
|
needAmr: number;
|
||||||
|
amrName: string;
|
||||||
|
status: TaskStatus;
|
||||||
|
stopReason?: string;
|
||||||
|
createBy: string;
|
||||||
|
createTime: string;
|
||||||
|
startTime?: string;
|
||||||
|
endTime?: string;
|
||||||
|
runDuration: number;
|
||||||
|
}
|
||||||
|
|
||||||
export default function RunScreen() {
|
export default function RunScreen() {
|
||||||
|
const [tasks, setTasks] = useState<TaskRunItem[]>([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
|
|
||||||
|
// 筛选状态
|
||||||
|
const [filterStatus, setFilterStatus] = useState<TaskStatus | undefined>(
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
const [filterName, setFilterName] = useState('');
|
||||||
|
|
||||||
|
// 操作状态
|
||||||
|
const [operatingTaskId, setOperatingTaskId] = useState<string | null>(null);
|
||||||
|
|
||||||
|
// 批量操作状态
|
||||||
|
const [selectedTasks, setSelectedTasks] = useState<Set<string>>(new Set());
|
||||||
|
const [isSelectionMode, setIsSelectionMode] = useState(false);
|
||||||
|
const [batchOperating, setBatchOperating] = useState(false);
|
||||||
|
|
||||||
|
// 加载任务列表
|
||||||
|
const loadTasks = useCallback(
|
||||||
|
async (isRefresh = false) => {
|
||||||
|
if (isRefresh) {
|
||||||
|
setRefreshing(true);
|
||||||
|
} else {
|
||||||
|
setLoading(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
status: filterStatus,
|
||||||
|
name: filterName.trim() || undefined,
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 100,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await getTaskRunList(params);
|
||||||
|
setTasks(response || []);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载任务列表失败:', error);
|
||||||
|
Alert.alert('错误', '加载任务列表失败,请重试');
|
||||||
|
setTasks([]);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
setRefreshing(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[filterStatus, filterName],
|
||||||
|
);
|
||||||
|
|
||||||
|
// 初始加载
|
||||||
|
useEffect(() => {
|
||||||
|
loadTasks();
|
||||||
|
}, [loadTasks]);
|
||||||
|
|
||||||
|
// 刷新任务列表
|
||||||
|
const handleRefresh = () => {
|
||||||
|
loadTasks(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重置筛选
|
||||||
|
const handleResetFilter = () => {
|
||||||
|
setFilterStatus(undefined);
|
||||||
|
setFilterName('');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 切换选择模式
|
||||||
|
const toggleSelectionMode = () => {
|
||||||
|
setIsSelectionMode(!isSelectionMode);
|
||||||
|
setSelectedTasks(new Set());
|
||||||
|
};
|
||||||
|
|
||||||
|
// 选择/取消选择任务
|
||||||
|
const toggleTaskSelection = (taskId: string) => {
|
||||||
|
const newSelection = new Set(selectedTasks);
|
||||||
|
if (newSelection.has(taskId)) {
|
||||||
|
newSelection.delete(taskId);
|
||||||
|
} else {
|
||||||
|
newSelection.add(taskId);
|
||||||
|
}
|
||||||
|
setSelectedTasks(newSelection);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 全选/取消全选
|
||||||
|
const toggleSelectAll = () => {
|
||||||
|
if (selectedTasks.size === tasks.length) {
|
||||||
|
setSelectedTasks(new Set());
|
||||||
|
} else {
|
||||||
|
setSelectedTasks(new Set(tasks.map(task => task.id)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 批量终止任务
|
||||||
|
const handleBatchTerminate = async () => {
|
||||||
|
if (selectedTasks.size === 0) {
|
||||||
|
Alert.alert('提示', '请先选择要终止的任务');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Alert.alert(
|
||||||
|
'确认操作',
|
||||||
|
`确定要终止选中的 ${selectedTasks.size} 个任务吗?`,
|
||||||
|
[
|
||||||
|
{ text: '取消', style: 'cancel' },
|
||||||
|
{
|
||||||
|
text: '确定',
|
||||||
|
style: 'destructive',
|
||||||
|
onPress: async () => {
|
||||||
|
setBatchOperating(true);
|
||||||
|
try {
|
||||||
|
const promises = Array.from(selectedTasks).map(taskId =>
|
||||||
|
terminateTask(taskId),
|
||||||
|
);
|
||||||
|
await Promise.all(promises);
|
||||||
|
Alert.alert('成功', `已成功终止 ${selectedTasks.size} 个任务`);
|
||||||
|
setSelectedTasks(new Set());
|
||||||
|
setIsSelectionMode(false);
|
||||||
|
loadTasks();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('批量终止任务失败:', error);
|
||||||
|
Alert.alert('错误', '批量终止任务失败,请重试');
|
||||||
|
} finally {
|
||||||
|
setBatchOperating(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 任务操作 - 只保留终止操作
|
||||||
|
const handleTaskOperation = async (taskId: string) => {
|
||||||
|
setOperatingTaskId(taskId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await terminateTask(taskId);
|
||||||
|
Alert.alert('成功', '终止任务成功');
|
||||||
|
// 重新加载任务列表
|
||||||
|
loadTasks();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('终止任务失败:', error);
|
||||||
|
Alert.alert('错误', '终止任务失败,请重试');
|
||||||
|
} finally {
|
||||||
|
setOperatingTaskId(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化时间
|
||||||
|
const formatTime = (timeString?: string) => {
|
||||||
|
if (!timeString) return '--';
|
||||||
|
try {
|
||||||
|
return new Date(timeString).toLocaleString('zh-CN', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return timeString;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 渲染任务项
|
||||||
|
const renderTaskItem = ({ item }: { item: TaskRunItem }) => {
|
||||||
|
const statusInfo =
|
||||||
|
statusDisplayMap[item.status] || statusDisplayMap[TaskStatus.WAITING];
|
||||||
|
const isOperating = operatingTaskId === item.id;
|
||||||
|
const isSelected = selectedTasks.has(item.id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.taskItem, isSelected ? styles.taskItemSelected : null]}
|
||||||
|
onPress={() => {
|
||||||
|
if (isSelectionMode) {
|
||||||
|
toggleTaskSelection(item.id);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
activeOpacity={isSelectionMode ? 0.7 : 1}
|
||||||
|
>
|
||||||
|
<View style={styles.taskHeader}>
|
||||||
|
<View style={styles.taskTitleContainer}>
|
||||||
|
{isSelectionMode && (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.checkboxContainer}
|
||||||
|
onPress={() => toggleTaskSelection(item.id)}
|
||||||
|
>
|
||||||
|
<MaterialIcons
|
||||||
|
name={isSelected ? 'check-box' : 'check-box-outline-blank'}
|
||||||
|
size={20}
|
||||||
|
color={isSelected ? '#4CAF50' : '#666'}
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.taskName,
|
||||||
|
isSelectionMode ? styles.taskNameWithCheckbox : null,
|
||||||
|
]}
|
||||||
|
numberOfLines={1}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</Text>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.statusBadge,
|
||||||
|
{ backgroundColor: statusInfo.color },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Text style={styles.statusText}>{statusInfo.text}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.taskDetails}>
|
||||||
|
<Text style={styles.taskDescription} numberOfLines={2}>
|
||||||
|
{item.description || '暂无描述'}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<View style={styles.timeContainer}>
|
||||||
|
<Text style={styles.timeLabel}>创建时间: </Text>
|
||||||
|
<Text style={styles.timeValue}>{formatTime(item.createTime)}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{item.startTime && (
|
||||||
|
<View style={styles.timeContainer}>
|
||||||
|
<Text style={styles.timeLabel}>开始时间: </Text>
|
||||||
|
<Text style={styles.timeValue}>{formatTime(item.startTime)}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{item.endTime && (
|
||||||
|
<View style={styles.timeContainer}>
|
||||||
|
<Text style={styles.timeLabel}>结束时间: </Text>
|
||||||
|
<Text style={styles.timeValue}>{formatTime(item.endTime)}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{!isSelectionMode && (
|
||||||
|
<View style={styles.actionContainer}>
|
||||||
|
{(item.status === TaskStatus.RUNNING ||
|
||||||
|
item.status === TaskStatus.PAUSED) && (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.actionButton, styles.terminateButton]}
|
||||||
|
onPress={() => handleTaskOperation(item.id)}
|
||||||
|
disabled={isOperating}
|
||||||
|
>
|
||||||
|
{isOperating ? (
|
||||||
|
<ActivityIndicator size="small" color="#ffffff" />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<MaterialIcons name="stop" size={16} color="#ffffff" />
|
||||||
|
<Text style={styles.actionButtonText}>终止</Text>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.screenContainer}>
|
<View style={styles.container}>
|
||||||
<Text style={styles.text}>任务运行状态列表</Text>
|
{/* 顶部操作区域 */}
|
||||||
<Text style={styles.subText}>此功能正在开发中...</Text>
|
<View style={styles.topActionContainer}>
|
||||||
|
<View style={styles.actionButtonsContainer}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[
|
||||||
|
styles.actionBtn,
|
||||||
|
isSelectionMode ? styles.actionBtnActive : null,
|
||||||
|
]}
|
||||||
|
onPress={toggleSelectionMode}
|
||||||
|
>
|
||||||
|
<MaterialIcons
|
||||||
|
name={isSelectionMode ? 'check-circle' : 'radio-button-unchecked'}
|
||||||
|
size={16}
|
||||||
|
color="#ffffff"
|
||||||
|
/>
|
||||||
|
<Text style={styles.actionBtnText}>
|
||||||
|
{isSelectionMode ? '取消选择' : '批量选择'}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
{isSelectionMode && (
|
||||||
|
<>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.actionBtn}
|
||||||
|
onPress={toggleSelectAll}
|
||||||
|
>
|
||||||
|
<MaterialIcons
|
||||||
|
name={
|
||||||
|
selectedTasks.size === tasks.length
|
||||||
|
? 'deselect'
|
||||||
|
: 'select-all'
|
||||||
|
}
|
||||||
|
size={16}
|
||||||
|
color="#ffffff"
|
||||||
|
/>
|
||||||
|
<Text style={styles.actionBtnText}>
|
||||||
|
{selectedTasks.size === tasks.length ? '取消全选' : '全选'}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.actionBtn, styles.terminateBtn]}
|
||||||
|
onPress={handleBatchTerminate}
|
||||||
|
disabled={selectedTasks.size === 0 || batchOperating}
|
||||||
|
>
|
||||||
|
{batchOperating ? (
|
||||||
|
<ActivityIndicator size="small" color="#ffffff" />
|
||||||
|
) : (
|
||||||
|
<MaterialIcons name="stop" size={16} color="#ffffff" />
|
||||||
|
)}
|
||||||
|
<Text style={styles.actionBtnText}>
|
||||||
|
终止 ({selectedTasks.size})
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 筛选区域 */}
|
||||||
|
<View style={styles.filterContainer}>
|
||||||
|
<View style={styles.filterRow}>
|
||||||
|
<View style={styles.statusFilterContainer}>
|
||||||
|
<Text style={styles.filterLabel}>任务状态:</Text>
|
||||||
|
<View style={styles.pickerContainer}>
|
||||||
|
<Picker
|
||||||
|
selectedValue={filterStatus}
|
||||||
|
onValueChange={value => setFilterStatus(value)}
|
||||||
|
style={styles.picker}
|
||||||
|
dropdownIconColor="#ffffff"
|
||||||
|
mode="dropdown"
|
||||||
|
>
|
||||||
|
{statusOptions.map(option => (
|
||||||
|
<Picker.Item
|
||||||
|
key={option.label}
|
||||||
|
label={option.label}
|
||||||
|
value={option.value}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Picker>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.resetButton}
|
||||||
|
onPress={handleResetFilter}
|
||||||
|
>
|
||||||
|
<MaterialIcons name="refresh" size={16} color="#ffffff" />
|
||||||
|
<Text style={styles.resetButtonText}>重置</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.nameFilterContainer}>
|
||||||
|
<TextInput
|
||||||
|
style={styles.nameInput}
|
||||||
|
placeholder="输入任务名称筛选..."
|
||||||
|
placeholderTextColor="#999"
|
||||||
|
value={filterName}
|
||||||
|
onChangeText={setFilterName}
|
||||||
|
returnKeyType="search"
|
||||||
|
onSubmitEditing={() => loadTasks()}
|
||||||
|
/>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.searchButton}
|
||||||
|
onPress={() => loadTasks()}
|
||||||
|
>
|
||||||
|
<MaterialIcons name="search" size={20} color="#ffffff" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 任务列表 */}
|
||||||
|
{loading ? (
|
||||||
|
<View style={styles.loadingContainer}>
|
||||||
|
<ActivityIndicator size="large" color="#2196F3" />
|
||||||
|
<Text style={styles.loadingText}>加载中...</Text>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<FlatList
|
||||||
|
data={tasks}
|
||||||
|
renderItem={renderTaskItem}
|
||||||
|
keyExtractor={item => item.id}
|
||||||
|
refreshing={refreshing}
|
||||||
|
onRefresh={handleRefresh}
|
||||||
|
ItemSeparatorComponent={() => <View style={styles.separator} />}
|
||||||
|
ListEmptyComponent={() => (
|
||||||
|
<View style={styles.emptyContainer}>
|
||||||
|
<MaterialIcons name="assignment" size={48} color="#666" />
|
||||||
|
<Text style={styles.emptyText}>暂无任务数据</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
contentContainerStyle={
|
||||||
|
tasks.length === 0 ? styles.emptyList : undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
screenContainer: {
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#121212',
|
||||||
|
},
|
||||||
|
filterContainer: {
|
||||||
|
backgroundColor: '#1a1a1a',
|
||||||
|
padding: 16,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: '#333',
|
||||||
|
},
|
||||||
|
filterRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
statusFilterContainer: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
filterLabel: {
|
||||||
|
color: '#ffffff',
|
||||||
|
fontSize: 14,
|
||||||
|
marginRight: 8,
|
||||||
|
},
|
||||||
|
pickerContainer: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#333',
|
||||||
|
borderRadius: 4,
|
||||||
|
marginRight: 12,
|
||||||
|
height: 40,
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
picker: {
|
||||||
|
color: '#ffffff',
|
||||||
|
height: 40,
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
resetButton: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: '#666',
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
paddingVertical: 8,
|
||||||
|
borderRadius: 4,
|
||||||
|
},
|
||||||
|
resetButtonText: {
|
||||||
|
color: '#ffffff',
|
||||||
|
fontSize: 12,
|
||||||
|
marginLeft: 4,
|
||||||
|
},
|
||||||
|
nameFilterContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
nameInput: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#333',
|
||||||
|
color: '#ffffff',
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
paddingVertical: 8,
|
||||||
|
borderRadius: 4,
|
||||||
|
marginRight: 8,
|
||||||
|
},
|
||||||
|
searchButton: {
|
||||||
|
backgroundColor: '#2196F3',
|
||||||
|
padding: 8,
|
||||||
|
borderRadius: 4,
|
||||||
|
},
|
||||||
|
loadingContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
backgroundColor: '#121212',
|
|
||||||
},
|
},
|
||||||
text: {
|
loadingText: {
|
||||||
fontSize: 22,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#ffffff',
|
color: '#ffffff',
|
||||||
marginBottom: 10,
|
marginTop: 8,
|
||||||
},
|
},
|
||||||
subText: {
|
taskItem: {
|
||||||
|
backgroundColor: '#1a1a1a',
|
||||||
|
padding: 16,
|
||||||
|
},
|
||||||
|
taskHeader: {
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
taskTitleContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
},
|
||||||
|
taskName: {
|
||||||
|
flex: 1,
|
||||||
|
color: '#ffffff',
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: '#b0b0b0',
|
fontWeight: 'bold',
|
||||||
|
marginRight: 8,
|
||||||
|
},
|
||||||
|
statusBadge: {
|
||||||
|
paddingHorizontal: 8,
|
||||||
|
paddingVertical: 4,
|
||||||
|
borderRadius: 12,
|
||||||
|
},
|
||||||
|
statusText: {
|
||||||
|
color: '#ffffff',
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
taskDetails: {
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
taskDescription: {
|
||||||
|
color: '#ccc',
|
||||||
|
fontSize: 14,
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
timeContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginBottom: 4,
|
||||||
|
},
|
||||||
|
timeLabel: {
|
||||||
|
color: '#999',
|
||||||
|
fontSize: 12,
|
||||||
|
minWidth: 60,
|
||||||
|
},
|
||||||
|
timeValue: {
|
||||||
|
color: '#ccc',
|
||||||
|
fontSize: 12,
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
actionContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
},
|
||||||
|
actionButton: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
paddingVertical: 6,
|
||||||
|
borderRadius: 4,
|
||||||
|
marginLeft: 8,
|
||||||
|
},
|
||||||
|
pauseButton: {
|
||||||
|
backgroundColor: '#FF9800',
|
||||||
|
},
|
||||||
|
continueButton: {
|
||||||
|
backgroundColor: '#4CAF50',
|
||||||
|
},
|
||||||
|
terminateButton: {
|
||||||
|
backgroundColor: '#F44336',
|
||||||
|
},
|
||||||
|
actionButtonText: {
|
||||||
|
color: '#ffffff',
|
||||||
|
fontSize: 12,
|
||||||
|
marginLeft: 4,
|
||||||
|
},
|
||||||
|
separator: {
|
||||||
|
height: 1,
|
||||||
|
backgroundColor: '#333',
|
||||||
|
},
|
||||||
|
emptyContainer: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingTop: 100,
|
||||||
|
},
|
||||||
|
emptyText: {
|
||||||
|
color: '#666',
|
||||||
|
fontSize: 16,
|
||||||
|
marginTop: 16,
|
||||||
|
},
|
||||||
|
emptyList: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
topActionContainer: {
|
||||||
|
backgroundColor: '#1a1a1a',
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
paddingVertical: 12,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: '#333',
|
||||||
|
},
|
||||||
|
actionButtonsContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
},
|
||||||
|
actionBtn: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: '#2196F3',
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
paddingVertical: 8,
|
||||||
|
borderRadius: 4,
|
||||||
|
marginRight: 8,
|
||||||
|
marginBottom: 4,
|
||||||
|
},
|
||||||
|
actionBtnActive: {
|
||||||
|
backgroundColor: '#4CAF50',
|
||||||
|
},
|
||||||
|
actionBtnText: {
|
||||||
|
color: '#ffffff',
|
||||||
|
fontSize: 12,
|
||||||
|
marginLeft: 4,
|
||||||
|
},
|
||||||
|
terminateBtn: {
|
||||||
|
backgroundColor: '#F44336',
|
||||||
|
},
|
||||||
|
taskItemSelected: {
|
||||||
|
backgroundColor: '#2a2a2a',
|
||||||
|
borderLeftWidth: 4,
|
||||||
|
borderLeftColor: '#4CAF50',
|
||||||
|
},
|
||||||
|
checkboxContainer: {
|
||||||
|
marginRight: 12,
|
||||||
|
padding: 4,
|
||||||
|
},
|
||||||
|
taskNameWithCheckbox: {
|
||||||
|
flex: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -56,16 +56,32 @@ api.interceptors.response.use(
|
|||||||
if (response.data && response.data.code && response.data.code !== 200) {
|
if (response.data && response.data.code && response.data.code !== 200) {
|
||||||
throw new Error(`API 错误: ${response.data.message}`);
|
throw new Error(`API 错误: ${response.data.message}`);
|
||||||
}
|
}
|
||||||
// 优先处理嵌套的 data 字段
|
|
||||||
if (response.data && typeof response.data.data !== 'undefined') {
|
// 处理不同的响应数据格式
|
||||||
// 如果 data 字段中有 list,则返回 list
|
if (response.data) {
|
||||||
if (Array.isArray(response.data.data.list)) {
|
// 格式1: { success: true, result: { records: [...] } } - 新的任务管理API格式
|
||||||
return response.data.data.list;
|
if (response.data.success && response.data.result) {
|
||||||
|
if (
|
||||||
|
response.data.result.records &&
|
||||||
|
Array.isArray(response.data.result.records)
|
||||||
|
) {
|
||||||
|
return response.data.result.records;
|
||||||
|
}
|
||||||
|
return response.data.result;
|
||||||
}
|
}
|
||||||
// 否则返回 data 字段本身
|
|
||||||
return response.data.data;
|
// 格式2: { data: { list: [...] } } - 原有格式
|
||||||
|
if (typeof response.data.data !== 'undefined') {
|
||||||
|
if (Array.isArray(response.data.data.list)) {
|
||||||
|
return response.data.data.list;
|
||||||
|
}
|
||||||
|
return response.data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式3: 直接返回整个响应数据
|
||||||
|
return response.data;
|
||||||
}
|
}
|
||||||
// 如果没有嵌套的 data 字段,直接返回整个响应数据
|
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
import { getConfig } from './configService';
|
import { getConfig } from './configService';
|
||||||
import { RunTaskRequest, RunTaskApiResponse, Task } from '../types/task';
|
import {
|
||||||
|
RunTaskRequest,
|
||||||
|
RunTaskApiResponse,
|
||||||
|
Task,
|
||||||
|
TaskStatus,
|
||||||
|
} from '../types/task';
|
||||||
import api from './api';
|
import api from './api';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
// 获取任务列表
|
// 获取任务列表
|
||||||
export const getTasks = async (params: {
|
export const getTasks = async (params: {
|
||||||
@ -52,3 +58,267 @@ export const runTask = async (
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ============ 任务管理相关API ============
|
||||||
|
|
||||||
|
// 创建任务管理API实例
|
||||||
|
const createTaskApi = () => {
|
||||||
|
return axios.create({
|
||||||
|
timeout: 10000,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 请求拦截器 - 为任务管理API配置
|
||||||
|
const setupTaskApiInterceptors = async (apiInstance: any) => {
|
||||||
|
const config = await getConfig();
|
||||||
|
|
||||||
|
apiInstance.interceptors.request.use(
|
||||||
|
async (requestConfig: any) => {
|
||||||
|
const taskServerUrl = config?.taskServerUrl;
|
||||||
|
if (!taskServerUrl) {
|
||||||
|
throw new Error('任务管理服务器地址未配置');
|
||||||
|
}
|
||||||
|
|
||||||
|
requestConfig.baseURL = taskServerUrl.replace(/\/$/, '');
|
||||||
|
requestConfig.headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...config?.headers,
|
||||||
|
...requestConfig.headers,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`[Task API Request] ${requestConfig.method?.toUpperCase()} ${
|
||||||
|
requestConfig.baseURL
|
||||||
|
}${requestConfig.url}`,
|
||||||
|
{ params: requestConfig.params, data: requestConfig.data },
|
||||||
|
);
|
||||||
|
return requestConfig;
|
||||||
|
},
|
||||||
|
(error: any) => {
|
||||||
|
console.error('[Task API Request Error]', error);
|
||||||
|
return Promise.reject(error);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
apiInstance.interceptors.response.use(
|
||||||
|
(response: any) => {
|
||||||
|
console.log(
|
||||||
|
`[Task API Response] ${response.config.method?.toUpperCase()} ${
|
||||||
|
response.config.url
|
||||||
|
}`,
|
||||||
|
response.data,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.data && response.data.code && response.data.code !== 200) {
|
||||||
|
throw new Error(`API 错误: ${response.data.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理任务管理API的响应格式: { success: true, result: { records: [...] } }
|
||||||
|
if (response.data && response.data.success && response.data.result) {
|
||||||
|
// 如果result中有records数组,返回records
|
||||||
|
if (
|
||||||
|
response.data.result.records &&
|
||||||
|
Array.isArray(response.data.result.records)
|
||||||
|
) {
|
||||||
|
return response.data.result.records;
|
||||||
|
}
|
||||||
|
// 否则返回整个result对象
|
||||||
|
return response.data.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
(error: any) => {
|
||||||
|
if (error.response) {
|
||||||
|
console.error(
|
||||||
|
`[Task API 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('[Task API Network Error]', error.request);
|
||||||
|
return Promise.reject(new Error('网络错误,请检查您的连接'));
|
||||||
|
} else {
|
||||||
|
console.error('[Task API Request Setup Error]', error.message);
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return apiInstance;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取任务运行列表
|
||||||
|
* @param params 查询参数
|
||||||
|
*/
|
||||||
|
export const getTaskRunList = async (params?: {
|
||||||
|
status?: TaskStatus;
|
||||||
|
name?: string;
|
||||||
|
pageNum?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
}): Promise<any[]> => {
|
||||||
|
try {
|
||||||
|
const config = await getConfig();
|
||||||
|
const endpoint = config?.taskApiEndpoints?.getTaskList;
|
||||||
|
if (!endpoint) throw new Error('获取任务列表的API端点未配置');
|
||||||
|
|
||||||
|
const taskApi = await setupTaskApiInterceptors(createTaskApi());
|
||||||
|
const response = await taskApi.get(endpoint, { params });
|
||||||
|
return response || [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取任务运行列表失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建任务
|
||||||
|
* @param params 任务参数
|
||||||
|
*/
|
||||||
|
export const createTaskRun = async (params: any): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const config = await getConfig();
|
||||||
|
const endpoint = config?.taskApiEndpoints?.createTask;
|
||||||
|
if (!endpoint) throw new Error('创建任务的API端点未配置');
|
||||||
|
|
||||||
|
const taskApi = await setupTaskApiInterceptors(createTaskApi());
|
||||||
|
const response = await taskApi.post(endpoint, params);
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('创建任务失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取任务运行详情
|
||||||
|
* @param id 任务ID
|
||||||
|
*/
|
||||||
|
export const getTaskRunDetail = async (id: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const config = await getConfig();
|
||||||
|
let endpoint = config?.taskApiEndpoints?.getTaskDetail;
|
||||||
|
if (!endpoint) throw new Error('获取任务详情的API端点未配置');
|
||||||
|
|
||||||
|
endpoint = endpoint.replace('{id}', id);
|
||||||
|
const taskApi = await setupTaskApiInterceptors(createTaskApi());
|
||||||
|
const response = await taskApi.get(endpoint);
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`获取任务运行详情失败 (ID: ${id}):`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取任务进度和动作列表
|
||||||
|
* @param ids 任务ID数组
|
||||||
|
*/
|
||||||
|
export const getTaskProgress = async (ids: string[]): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const config = await getConfig();
|
||||||
|
const endpoint = config?.taskApiEndpoints?.getTaskProgress;
|
||||||
|
if (!endpoint) throw new Error('获取任务进度的API端点未配置');
|
||||||
|
|
||||||
|
const taskApi = await setupTaskApiInterceptors(createTaskApi());
|
||||||
|
const response = await taskApi.get(endpoint, {
|
||||||
|
params: {
|
||||||
|
ids: ids.join(','),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取任务进度失败:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调整任务优先级
|
||||||
|
* @param id 任务ID
|
||||||
|
* @param priority 新优先级
|
||||||
|
*/
|
||||||
|
export const adjustTaskPriority = async (
|
||||||
|
id: string,
|
||||||
|
priority: number,
|
||||||
|
): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const config = await getConfig();
|
||||||
|
let endpoint = config?.taskApiEndpoints?.adjustTaskPriority;
|
||||||
|
if (!endpoint) throw new Error('调整任务优先级的API端点未配置');
|
||||||
|
|
||||||
|
endpoint = endpoint.replace('{id}', id);
|
||||||
|
const taskApi = await setupTaskApiInterceptors(createTaskApi());
|
||||||
|
const response = await taskApi.put(endpoint, { priority });
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`调整任务优先级失败 (ID: ${id}):`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 暂停任务
|
||||||
|
* @param id 任务ID
|
||||||
|
*/
|
||||||
|
export const pauseTask = async (id: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const config = await getConfig();
|
||||||
|
let endpoint = config?.taskApiEndpoints?.pauseTask;
|
||||||
|
if (!endpoint) throw new Error('暂停任务的API端点未配置');
|
||||||
|
|
||||||
|
endpoint = endpoint.replace('{id}', id);
|
||||||
|
const taskApi = await setupTaskApiInterceptors(createTaskApi());
|
||||||
|
const response = await taskApi.put(endpoint);
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`暂停任务失败 (ID: ${id}):`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 继续任务
|
||||||
|
* @param id 任务ID
|
||||||
|
*/
|
||||||
|
export const continueTask = async (id: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const config = await getConfig();
|
||||||
|
let endpoint = config?.taskApiEndpoints?.continueTask;
|
||||||
|
if (!endpoint) throw new Error('继续任务的API端点未配置');
|
||||||
|
|
||||||
|
endpoint = endpoint.replace('{id}', id);
|
||||||
|
const taskApi = await setupTaskApiInterceptors(createTaskApi());
|
||||||
|
const response = await taskApi.put(endpoint);
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`继续任务失败 (ID: ${id}):`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 终止任务
|
||||||
|
* @param id 任务ID
|
||||||
|
*/
|
||||||
|
export const terminateTask = async (id: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const config = await getConfig();
|
||||||
|
let endpoint = config?.taskApiEndpoints?.terminateTask;
|
||||||
|
if (!endpoint) throw new Error('终止任务的API端点未配置');
|
||||||
|
|
||||||
|
endpoint = endpoint.replace('{id}', id);
|
||||||
|
const taskApi = await setupTaskApiInterceptors(createTaskApi());
|
||||||
|
const response = await taskApi.put(endpoint);
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`终止任务失败 (ID: ${id}):`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -2,11 +2,23 @@
|
|||||||
export interface AppConfig {
|
export interface AppConfig {
|
||||||
version: string;
|
version: string;
|
||||||
serverUrl?: string; // 服务器地址
|
serverUrl?: string; // 服务器地址
|
||||||
|
taskServerUrl?: string; // 任务管理服务器地址
|
||||||
apiEndpoints?: {
|
apiEndpoints?: {
|
||||||
getTasks: string;
|
getTasks: string;
|
||||||
getTaskDetail: string;
|
getTaskDetail: string;
|
||||||
runTask: string;
|
runTask: string;
|
||||||
};
|
};
|
||||||
|
taskApiEndpoints?: {
|
||||||
|
getTaskList: string;
|
||||||
|
createTask: string;
|
||||||
|
getTaskDetail: string;
|
||||||
|
getTaskProgress: string;
|
||||||
|
adjustTaskPriority: string;
|
||||||
|
pauseTask: string;
|
||||||
|
continueTask: string;
|
||||||
|
terminateTask: string;
|
||||||
|
exportTask: string;
|
||||||
|
};
|
||||||
headers?: {
|
headers?: {
|
||||||
[key: string]: string | number;
|
[key: string]: string | number;
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* 任务状态枚举
|
||||||
|
*/
|
||||||
|
export enum TaskStatus {
|
||||||
|
WAITING = 0, // 待分配
|
||||||
|
RUNNING = 1, // 执行中
|
||||||
|
PAUSED = 2, // 已暂停
|
||||||
|
COMPLETED = 3, // 已完成
|
||||||
|
FAILED = 4, // 已失败
|
||||||
|
TERMINATED = 5, // 已终止
|
||||||
|
}
|
||||||
|
|
||||||
// 机器人的具体动作,现在是一个对象
|
// 机器人的具体动作,现在是一个对象
|
||||||
export interface RobotAction {
|
export interface RobotAction {
|
||||||
name: string;
|
name: string;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user