feat: 添加任务管理API支持,更新任务列表和运行屏幕以实现任务的创建、终止和状态管理功能

This commit is contained in:
xudan 2025-07-23 17:44:51 +08:00
parent ece6728e94
commit 97164ff220
7 changed files with 1002 additions and 62 deletions

View File

@ -1,6 +1,7 @@
{
"version": "1.0.0",
"serverUrl": "http://192.168.189.206:8000",
"taskServerUrl": "http://192.168.189.206:8080/jeecg-boot",
"headers": {
"x-access-token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NTIzMzQ5MjEsInVzZXJuYW1lIjoiYWRtaW4ifQ.dKvKIJOU5FxFeFNI3ucJTPLhHmUFZkv8HA2S_VO6klY",
"x-tenant-id": 1000
@ -9,5 +10,16 @@
"getTasks": "/api/vwed-task/list",
"getTaskDetail": "/api/vwed-task/{taskId}",
"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"
}
}

View File

@ -7,57 +7,27 @@ interface TaskCardProps {
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 statusInfo = statusMap[task.status] || statusMap[0];
return (
<TouchableOpacity onPress={() => onPress(task.id)} style={styles.container}>
<View style={styles.indicatorContainer}>
<View
style={[
styles.statusIndicator,
{ backgroundColor: statusInfo.color },
]}
/>
</View>
<View style={styles.contentContainer}>
<Text style={styles.title}>{task.label}</Text>
<Text style={styles.description} numberOfLines={1}>
<Text style={styles.description} numberOfLines={2}>
{task.description || '暂无描述'}
</Text>
</View>
<View style={styles.statusContainer}>
<Text style={[styles.statusText, { color: statusInfo.color }]}>
{statusInfo.text}
</Text>
</View>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#1a1a1a',
paddingVertical: 16,
paddingHorizontal: 16,
},
indicatorContainer: {
marginRight: 16,
},
statusIndicator: {
width: 12,
height: 12,
borderRadius: 6,
},
contentContainer: {
flex: 1,
},
@ -65,18 +35,12 @@ const styles = StyleSheet.create({
color: '#FFFFFF',
fontSize: 16,
fontWeight: 'bold',
marginBottom: 4,
marginBottom: 8,
},
description: {
color: '#999999',
fontSize: 14,
},
statusContainer: {
marginLeft: 16,
},
statusText: {
fontSize: 14,
fontWeight: 'bold',
lineHeight: 20,
},
});

View File

@ -1,30 +1,684 @@
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import React, { useState, useEffect, useCallback } from 'react';
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() {
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 (
<View style={styles.screenContainer}>
<Text style={styles.text}></Text>
<Text style={styles.subText}>...</Text>
<View style={styles.container}>
{/* 顶部操作区域 */}
<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>
);
}
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,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#121212',
},
text: {
fontSize: 22,
fontWeight: 'bold',
loadingText: {
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,
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,
},
});

View File

@ -56,16 +56,32 @@ api.interceptors.response.use(
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;
// 处理不同的响应数据格式
if (response.data) {
// 格式1: { success: true, result: { records: [...] } } - 新的任务管理API格式
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;
},
error => {

View File

@ -1,6 +1,12 @@
import { getConfig } from './configService';
import { RunTaskRequest, RunTaskApiResponse, Task } from '../types/task';
import {
RunTaskRequest,
RunTaskApiResponse,
Task,
TaskStatus,
} from '../types/task';
import api from './api';
import axios from 'axios';
// 获取任务列表
export const getTasks = async (params: {
@ -52,3 +58,267 @@ export const runTask = async (
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;
}
};

View File

@ -2,11 +2,23 @@
export interface AppConfig {
version: string;
serverUrl?: string; // 服务器地址
taskServerUrl?: string; // 任务管理服务器地址
apiEndpoints?: {
getTasks: string;
getTaskDetail: string;
runTask: string;
};
taskApiEndpoints?: {
getTaskList: string;
createTask: string;
getTaskDetail: string;
getTaskProgress: string;
adjustTaskPriority: string;
pauseTask: string;
continueTask: string;
terminateTask: string;
exportTask: string;
};
headers?: {
[key: string]: string | number;
};

View File

@ -1,3 +1,15 @@
/**
*
*/
export enum TaskStatus {
WAITING = 0, // 待分配
RUNNING = 1, // 执行中
PAUSED = 2, // 已暂停
COMPLETED = 3, // 已完成
FAILED = 4, // 已失败
TERMINATED = 5, // 已终止
}
// 机器人的具体动作,现在是一个对象
export interface RobotAction {
name: string;