task.js
import React from "react";
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
const Task = ({ text, color, onDelete, onPress }) => {
return (
<TouchableOpacity style={styles.item} onPress={onPress}>
<View style={styles.itemLeft}>
<View style={[styles.square, { backgroundColor: color }]}></View>
<Text style={styles.itemText}>{text}</Text>
</View>
<View style={styles.itemRight}>
<TouchableOpacity onPress={onDelete}>
<View style={styles.deleteWrapper}>
<Text style={(styles.deleteText, { color: color })}>√</Text>
</View>
</TouchableOpacity>
</View>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
item: {
backgroundColor: "#FFF",
padding: 15,
borderRadius: 10,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
marginHorizontal: 20,
marginBottom: 20,
},
itemLeft: {
flexDirection: "row",
alignItems: "center",
flexWrap: "wrap",
},
square: {
width: 24,
height: 24,
borderRadius: 5,
marginRight: 15,
},
itemText: {
maxWidth: "80%",
},
itemRight: {
flexDirection: "row",
alignItems: "center",
},
deleteWrapper: {
padding: 5,
},
deleteText: {
fontSize: 20,
},
});
export default Task;
todo.tsx
import React, { useRef, useState, useEffect } from "react";
import {
KeyboardAvoidingView,
StyleSheet,
Text,
View,
TextInput,
TouchableOpacity,
Keyboard,
ScrollView,
Platform,
ImageBackground,
Modal,
Button,
Animated,
} from "react-native";
import Task from "../components/Task"; // 引入 Task 组件
import { BlurView } from "expo-blur";
// 定义任务类型
type TaskItem = {
text: string;
};
const colors = [
"#55BCF6",
"#CB356B",
"#99f2c8",
"#91EAE4",
"#ee9ca7",
"#7F7FD5",
"#f5af19",
"#eaafc8",
"#654ea3",
];
const Todo: React.FC = () => {
// 任务的状态
const [task, setTask] = useState<string>("");
// 任务列表的状态
const [taskItems, setTaskItems] = useState<TaskItem[]>([]);
// 模态框的状态
const [modalVisible, setModalVisible] = useState(false);
// 当前编辑的任务索引
const [editIndex, setEditIndex] = useState<number | null>(null);
// 编辑任务的内容
const [editTask, setEditTask] = useState<string>("");
// 错误提示的状态
const [errorMessage, setErrorMessage] = useState<string | null>(null);
// 用于动画抖动效果
const shakeAnimation = useRef(new Animated.Value(0)).current;
useEffect(() => {
// 初始化任务列表
setTaskItems([
{ text: "Hello,here is LofiSu ~" },
{ text: "Let’s start!" },
]);
}, []); // 仅在组件首次加载时运行
// 添加任务的处理函数
const handleAddTask = () => {
Keyboard.dismiss(); // 隐藏键盘
if (task && task.trim()) {
// 确保任务不为空
setTaskItems([...taskItems, { text: task }]); // 添加新任务到任务列表
setTask(""); // 清空输入框
}
};
// 显示模态框并设置当前任务内容
const handleEditTask = (index: number) => {
setEditIndex(index);
setEditTask(taskItems[index].text);
setModalVisible(true);
};
// 抖动动画函数
const startShakeAnimation = () => {
shakeAnimation.setValue(0);
Animated.sequence([
Animated.timing(shakeAnimation, {
toValue: 10,
duration: 50,
useNativeDriver: true,
}),
Animated.timing(shakeAnimation, {
toValue: -10,
duration: 50,
useNativeDriver: true,
}),
Animated.timing(shakeAnimation, {
toValue: 10,
duration: 50,
useNativeDriver: true,
}),
Animated.timing(shakeAnimation, {
toValue: 0,
duration: 50,
useNativeDriver: true,
}),
]).start();
};
// 完成编辑任务的处理函数
const handleSaveEdit = () => {
if (editTask && editTask.trim()) {
if (editIndex !== null) {
const updatedTasks = [...taskItems];
updatedTasks[editIndex].text = editTask;
setTaskItems(updatedTasks);
setModalVisible(false);
setEditIndex(null);
setErrorMessage(null);
}
} else {
setErrorMessage("Content cannot be empty !");
startShakeAnimation();
}
};
// 取消编辑任务的处理函数
const handleCancelEdit = () => {
setModalVisible(false);
setEditIndex(null);
setErrorMessage(null);
};
// 完成任务的处理函数
const completeTask = (index: number) => {
let itemsCopy = [...taskItems]; // 复制任务列表
itemsCopy.splice(index, 1); // 删除指定任务
setTaskItems(itemsCopy); // 更新任务列表
};
return (
<ImageBackground
source={require("../../assets/images/background1.jpg")}
style={styles.background}
>
<BlurView intensity={30} style={styles.blurContainer}>
{/* 添加 ScrollView 以便列表项多于页面时可以滚动 */}
<ScrollView
contentContainerStyle={{
flexGrow: 1,
}}
keyboardShouldPersistTaps="handled"
>
<Text style={styles.sectionTitle}>Today's tasks</Text>
{/* 今日任务 */}
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : "height"}
style={styles.writeTaskWrapper}
>
<TextInput
style={styles.input}
placeholder={"Write a task"}
value={task || ""}
onChangeText={(text) => setTask(text)}
/>
<TouchableOpacity onPress={() => handleAddTask()}>
<View style={styles.addWrapper}>
<Text style={styles.addText}>+</Text>
</View>
</TouchableOpacity>
</KeyboardAvoidingView>
<View>
{taskItems.map((item, index) => (
<Task
key={index}
text={item.text}
color={colors[index % colors.length]} // 使用循环颜色
onDelete={() => completeTask(index)}
onPress={() => handleEditTask(index)}
/>
))}
</View>
</ScrollView>
{/* 编辑任务模态框 */}
<Modal transparent={true} visible={modalVisible} animationType="slide">
<View style={styles.modalContainer}>
<Animated.View
style={[
styles.modalContent,
{
transform: [
{
translateX: shakeAnimation,
},
],
},
]}
>
<Text style={styles.modalTitle}>Edit Task</Text>
<TextInput
style={styles.modalInput}
value={editTask || ""}
onChangeText={setEditTask}
placeholder="Edit your task"
/>
{errorMessage && (
<Text style={styles.errorText}>{errorMessage}</Text>
)}
<View style={styles.modalButtons}>
<Button title="Save" onPress={handleSaveEdit} />
<Button title="Cancel" onPress={handleCancelEdit} />
</View>
</Animated.View>
</View>
</Modal>
</BlurView>
</ImageBackground>
);
};
// 样式定义
const styles = StyleSheet.create({
background: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
blurContainer: {
flex: 1,
width: "100%",
height: "100%",
},
sectionTitle: {
fontSize: 24,
fontWeight: "bold",
color: "rgba(36, 59, 85, 1)",
marginTop: 20,
marginHorizontal: 20, // Adjust margin to ensure alignment
},
writeTaskWrapper: {
flexDirection: "row",
alignItems: "center",
marginHorizontal: 20,
marginBottom: 20,
marginTop: 20,
},
input: {
flex: 1,
paddingVertical: 10,
paddingHorizontal: 15,
backgroundColor: "#FFF",
borderRadius: 60,
borderColor: "#C0C0C0",
borderWidth: 1,
marginRight: 20, // Space between input and button
},
addWrapper: {
width: 40,
height: 40,
backgroundColor: "#FFF",
borderRadius: 50,
justifyContent: "center",
alignItems: "center",
borderColor: "#C0C0C0",
borderWidth: 1,
},
addText: {
fontSize: 20,
color: "rgba(36, 59, 85, 1)",
},
modalContainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "rgba(0, 0, 0, 0.5)",
},
modalContent: {
width: "80%",
padding: 20,
backgroundColor: "rgba(250, 250, 250, 0.9)",
borderRadius: 10,
alignItems: "center",
},
modalTitle: {
fontSize: 18,
fontWeight: "bold",
marginBottom: 10,
},
modalInput: {
width: "100%",
padding: 10,
borderColor: "rgba(0, 0, 0, 0.1)",
borderWidth: 1,
borderRadius: 5,
marginBottom: 20,
},
modalButtons: {
flexDirection: "row",
justifyContent: "space-between",
width: "100%",
},
errorText: {
color: "red",
marginBottom: 10,
},
});
export default Todo;