feat: 添加二维码扫描功能至任务编辑页面,支持解析二维码信息并自动填充表单,同时优化相关状态管理和动画效果
This commit is contained in:
parent
03f8e8d74a
commit
75ac33ca5d
@ -45,7 +45,7 @@ const TaskForm: React.FC<TaskFormProps> = ({ task, onTaskChange }) => {
|
|||||||
<View key={param.name} style={styles.inputGroup}>
|
<View key={param.name} style={styles.inputGroup}>
|
||||||
{label}
|
{label}
|
||||||
<Input
|
<Input
|
||||||
value={value as string}
|
value={String(value || '')}
|
||||||
onChangeText={text => handleParamChange(param.name, text)}
|
onChangeText={text => handleParamChange(param.name, text)}
|
||||||
placeholder={param.remark || '请输入...'}
|
placeholder={param.remark || '请输入...'}
|
||||||
inputContainerStyle={styles.inputContainer}
|
inputContainerStyle={styles.inputContainer}
|
||||||
|
@ -13,7 +13,7 @@ import { AppSettings } from '../types/config';
|
|||||||
import {
|
import {
|
||||||
getSettings,
|
getSettings,
|
||||||
saveSettings,
|
saveSettings,
|
||||||
downloadConfig,
|
// downloadConfig,
|
||||||
getConfig,
|
getConfig,
|
||||||
clearCachedConfig,
|
clearCachedConfig,
|
||||||
} from '../services/configService';
|
} from '../services/configService';
|
||||||
@ -46,11 +46,7 @@ export default function SettingsScreen() {
|
|||||||
const checkConfigStatus = async () => {
|
const checkConfigStatus = async () => {
|
||||||
const config = await getConfig();
|
const config = await getConfig();
|
||||||
if (config) {
|
if (config) {
|
||||||
setConfigStatus(
|
setConfigStatus(`已加载 (版本: ${config.version})`);
|
||||||
`已加载 (版本: ${config.version}, 任务数: ${
|
|
||||||
config.tasks?.length || 0
|
|
||||||
})`,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
setConfigStatus('未加载');
|
setConfigStatus('未加载');
|
||||||
}
|
}
|
||||||
@ -107,26 +103,20 @@ export default function SettingsScreen() {
|
|||||||
await saveSettings(settings);
|
await saveSettings(settings);
|
||||||
|
|
||||||
// 下载配置文件
|
// 下载配置文件
|
||||||
const config = await downloadConfig(
|
// const config = await downloadConfig(
|
||||||
settings.serverUrl,
|
// settings.serverUrl,
|
||||||
settings.configFileName,
|
// settings.configFileName,
|
||||||
);
|
// );
|
||||||
|
|
||||||
Alert.alert(
|
Alert.alert('成功', `配置文件下载成功!\n版本: `, [
|
||||||
'成功',
|
{
|
||||||
`配置文件下载成功!\n版本: ${config.version}\n任务数量: ${
|
text: '确定',
|
||||||
config.tasks?.length || 0
|
onPress: async () => {
|
||||||
}`,
|
await refreshConfig();
|
||||||
[
|
checkConfigStatus();
|
||||||
{
|
|
||||||
text: '确定',
|
|
||||||
onPress: async () => {
|
|
||||||
await refreshConfig();
|
|
||||||
checkConfigStatus();
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
);
|
]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
'下载失败',
|
'下载失败',
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
@ -6,7 +6,11 @@ import {
|
|||||||
ScrollView,
|
ScrollView,
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
Text,
|
Text,
|
||||||
|
TouchableOpacity,
|
||||||
|
TextInput,
|
||||||
|
Animated,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||||
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';
|
||||||
@ -36,6 +40,14 @@ export default function TaskEditScreen() {
|
|||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 添加二维码相关状态
|
||||||
|
const [qrCodeInfo, setQrCodeInfo] = useState<string>('');
|
||||||
|
const [isWaitingForQrCode, setIsWaitingForQrCode] = useState(true); // 默认为等待扫描状态
|
||||||
|
const qrCodeInputRef = useRef<TextInput>(null);
|
||||||
|
|
||||||
|
// 添加动画相关状态
|
||||||
|
const rotateAnim = useRef(new Animated.Value(0)).current;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadTask = async () => {
|
const loadTask = async () => {
|
||||||
let taskData = getTaskById(taskId);
|
let taskData = getTaskById(taskId);
|
||||||
@ -64,6 +76,121 @@ export default function TaskEditScreen() {
|
|||||||
loadTask();
|
loadTask();
|
||||||
}, [taskId, getTaskById, tasks, fetchTaskDetail]);
|
}, [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) => {
|
const handleTaskChange = (updatedTask: Task) => {
|
||||||
setTask(updatedTask);
|
setTask(updatedTask);
|
||||||
if (!isModified) {
|
if (!isModified) {
|
||||||
@ -71,6 +198,17 @@ export default function TaskEditScreen() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 处理二维码扫描按钮点击
|
||||||
|
const handleQrCodeScanButton = () => {
|
||||||
|
if (!isWaitingForQrCode) {
|
||||||
|
setIsWaitingForQrCode(true);
|
||||||
|
// 聚焦到二维码输入框 (清空操作已在useEffect中处理)
|
||||||
|
setTimeout(() => {
|
||||||
|
qrCodeInputRef.current?.focus();
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
if (task) {
|
if (task) {
|
||||||
updateTask(task);
|
updateTask(task);
|
||||||
@ -90,14 +228,25 @@ export default function TaskEditScreen() {
|
|||||||
setRunResponse(null);
|
setRunResponse(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const params = task.detail.inputParams.map(p => ({
|
const params = task.detail.inputParams.map(p => {
|
||||||
name: p.name,
|
// 获取用户设置的实际值,如果用户没有设置任何值才使用默认值
|
||||||
type: p.type,
|
const parameter = task.parameters?.[p.name];
|
||||||
label: p.label,
|
const hasUserInput = parameter !== undefined;
|
||||||
required: p.required,
|
const actualValue = hasUserInput ? parameter.value : p.defaultValue;
|
||||||
defaultValue: p.defaultValue,
|
|
||||||
remark: p.remark,
|
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 = {
|
const request: RunTaskRequest = {
|
||||||
taskId: task.id,
|
taskId: task.id,
|
||||||
@ -120,6 +269,9 @@ export default function TaskEditScreen() {
|
|||||||
const handleUndo = () => {
|
const handleUndo = () => {
|
||||||
setTask(originalTask);
|
setTask(originalTask);
|
||||||
setIsModified(false);
|
setIsModified(false);
|
||||||
|
// 重置二维码相关状态
|
||||||
|
setQrCodeInfo('');
|
||||||
|
setIsWaitingForQrCode(true); // 重置为默认的等待扫描状态
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!task) {
|
if (!task) {
|
||||||
@ -133,6 +285,65 @@ export default function TaskEditScreen() {
|
|||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<ScrollView contentContainerStyle={styles.scrollContainer}>
|
<ScrollView contentContainerStyle={styles.scrollContainer}>
|
||||||
|
{/* 二维码扫描按钮 */}
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[
|
||||||
|
styles.qrCodeButton,
|
||||||
|
isWaitingForQrCode
|
||||||
|
? styles.qrCodeButtonWaiting
|
||||||
|
: styles.qrCodeButtonReady,
|
||||||
|
]}
|
||||||
|
onPress={handleQrCodeScanButton}
|
||||||
|
>
|
||||||
|
<View style={styles.qrCodeButtonContent}>
|
||||||
|
{isWaitingForQrCode ? (
|
||||||
|
<Animated.View
|
||||||
|
style={[
|
||||||
|
styles.qrCodeButtonIcon,
|
||||||
|
{
|
||||||
|
transform: [
|
||||||
|
{
|
||||||
|
rotate: rotateAnim.interpolate({
|
||||||
|
inputRange: [0, 1],
|
||||||
|
outputRange: ['0deg', '360deg'],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Icon name="hourglass-empty" size={20} color="#ffffff" />
|
||||||
|
</Animated.View>
|
||||||
|
) : (
|
||||||
|
<Icon
|
||||||
|
name="qr-code"
|
||||||
|
size={20}
|
||||||
|
color="#ffffff"
|
||||||
|
style={styles.qrCodeButtonIcon}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Text style={styles.qrCodeButtonText}>
|
||||||
|
{isWaitingForQrCode ? '等待扫描二维码' : '点击启动扫码'}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
{/* 隐藏的二维码信息输入框 */}
|
||||||
|
<TextInput
|
||||||
|
ref={qrCodeInputRef}
|
||||||
|
style={styles.hiddenQrCodeInput}
|
||||||
|
value={qrCodeInfo}
|
||||||
|
onChangeText={handleQrCodeChange}
|
||||||
|
onFocus={() => setIsWaitingForQrCode(true)}
|
||||||
|
onBlur={() => setIsWaitingForQrCode(false)}
|
||||||
|
placeholder="二维码信息将在此处显示"
|
||||||
|
multiline={true}
|
||||||
|
autoFocus={isWaitingForQrCode}
|
||||||
|
showSoftInputOnFocus={false}
|
||||||
|
caretHidden={true}
|
||||||
|
editable={true}
|
||||||
|
/>
|
||||||
|
|
||||||
<TaskForm task={task} onTaskChange={handleTaskChange} />
|
<TaskForm task={task} onTaskChange={handleTaskChange} />
|
||||||
{runResponse && (
|
{runResponse && (
|
||||||
<View style={styles.responseContainer}>
|
<View style={styles.responseContainer}>
|
||||||
@ -170,6 +381,39 @@ const styles = StyleSheet.create({
|
|||||||
padding: 16,
|
padding: 16,
|
||||||
paddingBottom: 80,
|
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: {
|
responseContainer: {
|
||||||
marginTop: 20,
|
marginTop: 20,
|
||||||
padding: 15,
|
padding: 15,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user