From 75ac33ca5d8e64d40a281358747ce9cb4b9d6e8e Mon Sep 17 00:00:00 2001 From: xudan Date: Thu, 24 Jul 2025 14:40:40 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=BA=8C=E7=BB=B4?= =?UTF-8?q?=E7=A0=81=E6=89=AB=E6=8F=8F=E5=8A=9F=E8=83=BD=E8=87=B3=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E7=BC=96=E8=BE=91=E9=A1=B5=E9=9D=A2=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E8=A7=A3=E6=9E=90=E4=BA=8C=E7=BB=B4=E7=A0=81=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E5=B9=B6=E8=87=AA=E5=8A=A8=E5=A1=AB=E5=85=85=E8=A1=A8?= =?UTF-8?q?=E5=8D=95=EF=BC=8C=E5=90=8C=E6=97=B6=E4=BC=98=E5=8C=96=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E7=8A=B6=E6=80=81=E7=AE=A1=E7=90=86=E5=92=8C=E5=8A=A8?= =?UTF-8?q?=E7=94=BB=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/TaskForm.tsx | 2 +- src/screens/SettingsScreen.tsx | 38 ++--- src/screens/TaskEditScreen.tsx | 262 +++++++++++++++++++++++++++++++-- 3 files changed, 268 insertions(+), 34 deletions(-) diff --git a/src/components/TaskForm.tsx b/src/components/TaskForm.tsx index 1bc5933..619f5ad 100644 --- a/src/components/TaskForm.tsx +++ b/src/components/TaskForm.tsx @@ -45,7 +45,7 @@ const TaskForm: React.FC = ({ task, onTaskChange }) => { {label} handleParamChange(param.name, text)} placeholder={param.remark || '请输入...'} inputContainerStyle={styles.inputContainer} diff --git a/src/screens/SettingsScreen.tsx b/src/screens/SettingsScreen.tsx index 30cc887..1d1a22b 100644 --- a/src/screens/SettingsScreen.tsx +++ b/src/screens/SettingsScreen.tsx @@ -13,7 +13,7 @@ import { AppSettings } from '../types/config'; import { getSettings, saveSettings, - downloadConfig, + // downloadConfig, getConfig, clearCachedConfig, } from '../services/configService'; @@ -46,11 +46,7 @@ export default function SettingsScreen() { const checkConfigStatus = async () => { const config = await getConfig(); if (config) { - setConfigStatus( - `已加载 (版本: ${config.version}, 任务数: ${ - config.tasks?.length || 0 - })`, - ); + setConfigStatus(`已加载 (版本: ${config.version})`); } else { setConfigStatus('未加载'); } @@ -107,26 +103,20 @@ export default function SettingsScreen() { await saveSettings(settings); // 下载配置文件 - const config = await downloadConfig( - settings.serverUrl, - settings.configFileName, - ); + // const config = await downloadConfig( + // settings.serverUrl, + // settings.configFileName, + // ); - Alert.alert( - '成功', - `配置文件下载成功!\n版本: ${config.version}\n任务数量: ${ - config.tasks?.length || 0 - }`, - [ - { - text: '确定', - onPress: async () => { - await refreshConfig(); - checkConfigStatus(); - }, + Alert.alert('成功', `配置文件下载成功!\n版本: `, [ + { + text: '确定', + onPress: async () => { + await refreshConfig(); + checkConfigStatus(); }, - ], - ); + }, + ]); } catch (error) { Alert.alert( '下载失败', diff --git a/src/screens/TaskEditScreen.tsx b/src/screens/TaskEditScreen.tsx index 410691b..5ed98b2 100644 --- a/src/screens/TaskEditScreen.tsx +++ b/src/screens/TaskEditScreen.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import { View, StyleSheet, @@ -6,7 +6,11 @@ import { ScrollView, ActivityIndicator, Text, + TouchableOpacity, + TextInput, + Animated, } from 'react-native'; +import Icon from 'react-native-vector-icons/MaterialIcons'; import { useRoute, useNavigation } from '@react-navigation/native'; import { RouteProp } from '@react-navigation/native'; import { useTasks } from '../context/TasksContext'; @@ -36,6 +40,14 @@ export default function TaskEditScreen() { null, ); + // 添加二维码相关状态 + const [qrCodeInfo, setQrCodeInfo] = useState(''); + const [isWaitingForQrCode, setIsWaitingForQrCode] = useState(true); // 默认为等待扫描状态 + const qrCodeInputRef = useRef(null); + + // 添加动画相关状态 + const rotateAnim = useRef(new Animated.Value(0)).current; + useEffect(() => { const loadTask = async () => { let taskData = getTaskById(taskId); @@ -64,6 +76,121 @@ export default function TaskEditScreen() { loadTask(); }, [taskId, getTaskById, tasks, fetchTaskDetail]); + // 聚焦到二维码输入框和启动动画 + useEffect(() => { + if (isWaitingForQrCode && qrCodeInputRef.current) { + // 清空二维码信息输入框 + setQrCodeInfo(''); + setTimeout(() => { + qrCodeInputRef.current?.focus(); + }, 500); + } + + // 启动或停止旋转动画 + if (isWaitingForQrCode) { + // 启动旋转动画 + const rotateAnimation = Animated.loop( + Animated.timing(rotateAnim, { + toValue: 1, + duration: 2000, + useNativeDriver: true, + }), + ); + rotateAnimation.start(); + + return () => { + rotateAnimation.stop(); + }; + } else { + // 停止动画并重置 + rotateAnim.setValue(0); + } + }, [isWaitingForQrCode, rotateAnim]); + + // 解析二维码信息并填充表单 + const parseQrCodeAndFillForm = (qrContent: string) => { + if ( + !task || + !task.detail || + !task.detail.inputParams || + !qrContent.trim() + ) { + return; + } + + try { + // 清理二维码内容:移除换行符和多余空格 + const cleanedContent = qrContent + .replace(/[\r\n\t]/g, '') // 移除所有换行符和制表符 + .replace(/\s+/g, ' ') // 多个空格替换为单个空格 + .trim(); + + const qrData = JSON.parse(cleanedContent); + console.log('二维码解析成功:', qrData); + + // 创建新的任务对象,保持原有数据不变 + const updatedTask = { ...task }; + + // 确保parameters对象存在 + if (!updatedTask.parameters) { + updatedTask.parameters = {}; + } + + // 先清空所有表单字段为空字符串 + task.detail!.inputParams.forEach(param => { + const currentParam = updatedTask.parameters[param.name] || {}; + updatedTask.parameters[param.name] = { + ...currentParam, + value: '', // 清空为空字符串 + }; + }); + + // 然后用二维码数据填充匹配的字段 + Object.keys(qrData).forEach(qrKey => { + const matchingParam = task.detail!.inputParams.find( + param => param.name === qrKey, + ); + + if (matchingParam) { + // 根据字段类型转换值 + let convertedValue = qrData[qrKey]; + if (matchingParam.type.toLowerCase() === 'string') { + convertedValue = String(qrData[qrKey]); + } else if (matchingParam.type.toLowerCase() === 'number') { + convertedValue = Number(qrData[qrKey]); + } + + // 如果找到匹配的参数,更新其值 + const currentParam = updatedTask.parameters[qrKey] || {}; + updatedTask.parameters[qrKey] = { + ...currentParam, + value: convertedValue, + }; + console.log(`二维码填充: ${qrKey} = ${convertedValue}`); + } + }); + + setTask(updatedTask); + if (!isModified) { + setIsModified(true); + } + + // 扫描成功后,按钮状态变回"扫描二维码获取信息" + setIsWaitingForQrCode(false); + } catch (error) { + // 解析失败不报错,按照需求静默处理 + console.log('二维码信息解析失败,但不影响使用:', error); + } + }; + + // 处理二维码输入框内容变化 + const handleQrCodeChange = (text: string) => { + setQrCodeInfo(text); + if (text.trim()) { + parseQrCodeAndFillForm(text); + } + }; + const handleTaskChange = (updatedTask: Task) => { setTask(updatedTask); if (!isModified) { @@ -71,6 +198,17 @@ export default function TaskEditScreen() { } }; + // 处理二维码扫描按钮点击 + const handleQrCodeScanButton = () => { + if (!isWaitingForQrCode) { + setIsWaitingForQrCode(true); + // 聚焦到二维码输入框 (清空操作已在useEffect中处理) + setTimeout(() => { + qrCodeInputRef.current?.focus(); + }, 100); + } + }; + const handleSave = () => { if (task) { updateTask(task); @@ -90,14 +228,25 @@ export default function TaskEditScreen() { 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 params = task.detail.inputParams.map(p => { + // 获取用户设置的实际值,如果用户没有设置任何值才使用默认值 + const parameter = task.parameters?.[p.name]; + const hasUserInput = parameter !== undefined; + const actualValue = hasUserInput ? parameter.value : p.defaultValue; + + console.log( + `运行参数 ${p.name}: 用户是否输入=${hasUserInput}, 实际值="${actualValue}" (原默认值="${p.defaultValue}")`, + ); + + return { + name: p.name, + type: p.type, + label: p.label, + required: p.required, + defaultValue: actualValue, // 如果用户有输入(包括空值)就用用户值,否则用默认值 + remark: p.remark, + }; + }); const request: RunTaskRequest = { taskId: task.id, @@ -120,6 +269,9 @@ export default function TaskEditScreen() { const handleUndo = () => { setTask(originalTask); setIsModified(false); + // 重置二维码相关状态 + setQrCodeInfo(''); + setIsWaitingForQrCode(true); // 重置为默认的等待扫描状态 }; if (!task) { @@ -133,6 +285,65 @@ export default function TaskEditScreen() { return ( + {/* 二维码扫描按钮 */} + + + {isWaitingForQrCode ? ( + + + + ) : ( + + )} + + {isWaitingForQrCode ? '等待扫描二维码' : '点击启动扫码'} + + + + + {/* 隐藏的二维码信息输入框 */} + setIsWaitingForQrCode(true)} + onBlur={() => setIsWaitingForQrCode(false)} + placeholder="二维码信息将在此处显示" + multiline={true} + autoFocus={isWaitingForQrCode} + showSoftInputOnFocus={false} + caretHidden={true} + editable={true} + /> + {runResponse && ( @@ -170,6 +381,39 @@ const styles = StyleSheet.create({ padding: 16, paddingBottom: 80, }, + qrCodeButton: { + padding: 12, + borderRadius: 8, + marginBottom: 16, + alignItems: 'center', + }, + qrCodeButtonWaiting: { + backgroundColor: '#2196F3', + }, + qrCodeButtonReady: { + backgroundColor: '#4CAF50', + }, + qrCodeButtonContent: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + }, + qrCodeButtonIcon: { + marginRight: 8, + }, + qrCodeButtonText: { + color: '#ffffff', + fontSize: 16, + fontWeight: 'bold', + }, + hiddenQrCodeInput: { + position: 'absolute', + top: -1000, + left: -1000, + width: 1, + height: 1, + opacity: 0, + }, responseContainer: { marginTop: 20, padding: 15,