├── .gitignore ├── .vscode └── settings.json ├── README.md ├── app.json ├── app ├── (tabs) │ ├── _layout.tsx │ ├── index.tsx │ ├── two.tsx │ └── welcome.tsx ├── +html.tsx ├── +not-found.tsx ├── Timer.tsx ├── _layout.tsx ├── components │ ├── AnalogClock.jsx │ ├── Task.js │ ├── TimeWeather.tsx │ └── weather.tsx ├── modal.tsx └── styles │ └── screen-style.ts ├── assets ├── fonts │ └── SpaceMono-Regular.ttf └── images │ ├── adaptive-icon.png │ ├── background1.jpg │ ├── background2.jpg │ ├── background3.jpg │ ├── background4.jpg │ ├── background5.jpg │ ├── background6.jpg │ ├── favicon.png │ ├── icon.png │ └── splash.png ├── babel.config.js ├── components ├── EditScreenInfo.tsx ├── ExternalLink.tsx ├── StyledText.tsx ├── Themed.tsx ├── __tests__ │ └── StyledText-test.js ├── useClientOnlyValue.ts ├── useClientOnlyValue.web.ts ├── useColorScheme.ts └── useColorScheme.web.ts ├── constants └── Colors.ts ├── package-lock.json ├── package.json └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files 2 | 3 | # dependencies 4 | node_modules/ 5 | 6 | # Expo 7 | .expo/ 8 | dist/ 9 | web-build/ 10 | 11 | # Native 12 | *.orig.* 13 | *.jks 14 | *.p8 15 | *.p12 16 | *.key 17 | *.mobileprovision 18 | 19 | # Metro 20 | .metro-health-check* 21 | 22 | # debug 23 | npm-debug.* 24 | yarn-debug.* 25 | yarn-error.* 26 | 27 | # macOS 28 | .DS_Store 29 | *.pem 30 | 31 | # local env files 32 | .env*.local 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | 37 | # @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb 38 | # The following patterns were generated by expo-cli 39 | 40 | expo-env.d.ts 41 | # @end expo-cli -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "winddirection" 4 | ] 5 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tick-Clock 2 | ## It is a time management software supports web, ios, and Android. 3 | ## Your daily life partner. 4 | 5 | ## 我的大一课设: 6 | # 滴答时钟--是一个简洁的时间管家。 7 | ## Functions: 8 | ### 1.每隔一分钟切换一次壁纸,支持自定义壁纸,支持自定义头像。 9 | ### 2.点击每日一言切换名言。(我用的是一言的api可以自定义) 10 | ### 3.点击专注时钟按钮到专注计时页面。 11 | ### 4.时钟界面显示数字时钟和圆盘时钟,显示日期星期,根据所在定位显示所在地天气。(支持自定义切换API ,我用的是高德地图的api) 12 | ### 5.To DO List界面可编辑当日的Task,默认有两个task,你可以删掉。 13 | ### 6.点击完成√按钮删除task,点击task本身编辑内容,内容不能为空。 14 | ### 7.modal内容为About Me。可以跳转链接到我的github主页,我的home主页,我的博客。 15 | 16 | 17 | ### ScreenShoots here: 18 | ![50e87ee2bd8e8e8f80926e063ebedc6](https://github.com/user-attachments/assets/6518dfb5-2414-46d6-aecb-55756ab0a166) 19 | ![b58dceb182081e83f2825fff43888e2](https://github.com/user-attachments/assets/e87d1a40-5c3b-4a47-b96a-158a3d64be83) 20 | ![f971dbc03727429a0d025d63e84927f](https://github.com/user-attachments/assets/ca5f70a1-2aee-49e7-a5df-5ddc0ebbada6) 21 | ![735edd312c949a7375c527d83833243](https://github.com/user-attachments/assets/d7934583-1177-45f9-8b5c-f916c598baca) 22 | ![f5b4b8e6ed5e9e7966e0707ffb6b14b](https://github.com/user-attachments/assets/b4f13040-c4cd-4bb6-84e7-982267914854) 23 | ![de050080c0c5c1ed5d1c920723fbbab](https://github.com/user-attachments/assets/2d016e46-62f3-461a-a9a6-8d6420d8f819) 24 | ![59bf65567eb70001f5bbf6660930c04](https://github.com/user-attachments/assets/83ac87da-5869-4a4d-a7ae-6c704c96219f) 25 | ![312b6cc9d2e76c845b35815fe6219c0](https://github.com/user-attachments/assets/e11c9b51-d3d9-41c7-856a-ad016cdc5aed) 26 | ![2cf0d154fa16a7cf7afa378c1c296d8](https://github.com/user-attachments/assets/218ed5e4-ee7b-4667-a6fd-f8eb7d3eea80) 27 | 28 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "tomato-clock", 4 | "slug": "tomato-clock", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/images/icon.png", 8 | "scheme": "myapp", 9 | "userInterfaceStyle": "automatic", 10 | "splash": { 11 | "image": "./assets/images/splash.png", 12 | "resizeMode": "contain", 13 | "backgroundColor": "#ffffff" 14 | }, 15 | "ios": { 16 | "supportsTablet": true 17 | }, 18 | "android": { 19 | "adaptiveIcon": { 20 | "foregroundImage": "./assets/images/adaptive-icon.png", 21 | "backgroundColor": "#ffffff" 22 | } 23 | }, 24 | "web": { 25 | "bundler": "metro", 26 | "output": "static", 27 | "favicon": "./assets/images/favicon.png" 28 | }, 29 | "plugins": [ 30 | "expo-router", 31 | [ 32 | "expo-calendar", 33 | { 34 | "calendarPermission": "The app needs to access your calendar." 35 | } 36 | ], 37 | "expo-font" 38 | ], 39 | "experiments": { 40 | "typedRoutes": true 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/(tabs)/_layout.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import FontAwesome from "@expo/vector-icons/FontAwesome"; 3 | import { Link, Tabs } from "expo-router"; 4 | import { Pressable } from "react-native"; 5 | import AntDesign from "@expo/vector-icons/AntDesign"; 6 | import Octicons from "@expo/vector-icons/Octicons"; 7 | 8 | import Colors from "@/constants/Colors"; 9 | import { useColorScheme } from "@/components/useColorScheme"; 10 | import { useClientOnlyValue } from "@/components/useClientOnlyValue"; 11 | 12 | // You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/ 13 | function TabBarIcon(props: { 14 | name: React.ComponentProps["name"]; 15 | color: string; 16 | }) { 17 | return ; 18 | } 19 | 20 | export default function TabLayout() { 21 | const colorScheme = useColorScheme(); 22 | 23 | return ( 24 | 33 | ( 38 | 39 | ), 40 | headerRight: () => ( 41 | 42 | 43 | {({ pressed }) => ( 44 | 50 | )} 51 | 52 | 53 | ), 54 | }} 55 | /> 56 | ( 61 | 62 | ), 63 | headerRight: () => ( 64 | 65 | 66 | {({ pressed }) => ( 67 | 73 | )} 74 | 75 | 76 | ), 77 | }} 78 | /> 79 | ( 84 | 85 | ), 86 | headerRight: () => ( 87 | 88 | 89 | {({ pressed }) => ( 90 | 96 | )} 97 | 98 | 99 | ), 100 | }} 101 | /> 102 | 103 | ); 104 | } 105 | -------------------------------------------------------------------------------- /app/(tabs)/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import AnalogClock from "../components/AnalogClock"; 3 | import { ImageBackground, ScrollView, View, StyleSheet } from "react-native"; 4 | import TimeWeather from "../components/TimeWeather"; 5 | import { BlurView } from "expo-blur"; 6 | 7 | const index = () => { 8 | return ( 9 | 13 | 14 | 20 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ); 34 | }; 35 | 36 | const styles = StyleSheet.create({ 37 | background: { 38 | flex: 1, 39 | justifyContent: "center", 40 | alignItems: "center", 41 | }, 42 | blurContainer: { 43 | flex: 1, 44 | justifyContent: "center", 45 | alignItems: "center", 46 | width: "100%", 47 | height: "100%", 48 | }, 49 | }); 50 | 51 | export default index; 52 | -------------------------------------------------------------------------------- /app/(tabs)/two.tsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useState, useEffect } from "react"; 2 | import { 3 | KeyboardAvoidingView, 4 | StyleSheet, 5 | Text, 6 | View, 7 | TextInput, 8 | TouchableOpacity, 9 | Keyboard, 10 | ScrollView, 11 | Platform, 12 | ImageBackground, 13 | Modal, 14 | Button, 15 | Animated, 16 | } from "react-native"; 17 | import Task from "../components/Task"; // 引入 Task 组件 18 | import { BlurView } from "expo-blur"; 19 | 20 | // 定义任务类型 21 | type TaskItem = { 22 | text: string; 23 | }; 24 | 25 | const colors = [ 26 | "#55BCF6", 27 | "#CB356B", 28 | "#99f2c8", 29 | "#91EAE4", 30 | "#ee9ca7", 31 | "#7F7FD5", 32 | "#f5af19", 33 | "#eaafc8", 34 | "#654ea3", 35 | ]; 36 | 37 | const Todo: React.FC = () => { 38 | // 任务的状态 39 | const [task, setTask] = useState(""); 40 | // 任务列表的状态 41 | const [taskItems, setTaskItems] = useState([]); 42 | // 模态框的状态 43 | const [modalVisible, setModalVisible] = useState(false); 44 | // 当前编辑的任务索引 45 | const [editIndex, setEditIndex] = useState(null); 46 | // 编辑任务的内容 47 | const [editTask, setEditTask] = useState(""); 48 | // 错误提示的状态 49 | const [errorMessage, setErrorMessage] = useState(null); 50 | // 用于动画抖动效果 51 | const shakeAnimation = useRef(new Animated.Value(0)).current; 52 | 53 | useEffect(() => { 54 | // 初始化任务列表 55 | setTaskItems([ 56 | { text: "Hello,here is LofiSu ~" }, 57 | { text: "Let’s start!" }, 58 | ]); 59 | }, []); // 仅在组件首次加载时运行 60 | 61 | // 添加任务的处理函数 62 | const handleAddTask = () => { 63 | Keyboard.dismiss(); // 隐藏键盘 64 | if (task && task.trim()) { 65 | // 确保任务不为空 66 | setTaskItems([...taskItems, { text: task }]); // 添加新任务到任务列表 67 | setTask(""); // 清空输入框 68 | } 69 | }; 70 | 71 | // 显示模态框并设置当前任务内容 72 | const handleEditTask = (index: number) => { 73 | setEditIndex(index); 74 | setEditTask(taskItems[index].text); 75 | setModalVisible(true); 76 | }; 77 | 78 | // 抖动动画函数 79 | const startShakeAnimation = () => { 80 | shakeAnimation.setValue(0); 81 | Animated.sequence([ 82 | Animated.timing(shakeAnimation, { 83 | toValue: 10, 84 | duration: 50, 85 | useNativeDriver: true, 86 | }), 87 | Animated.timing(shakeAnimation, { 88 | toValue: -10, 89 | duration: 50, 90 | useNativeDriver: true, 91 | }), 92 | Animated.timing(shakeAnimation, { 93 | toValue: 10, 94 | duration: 50, 95 | useNativeDriver: true, 96 | }), 97 | Animated.timing(shakeAnimation, { 98 | toValue: 0, 99 | duration: 50, 100 | useNativeDriver: true, 101 | }), 102 | ]).start(); 103 | }; 104 | 105 | // 完成编辑任务的处理函数 106 | const handleSaveEdit = () => { 107 | if (editTask && editTask.trim()) { 108 | if (editIndex !== null) { 109 | const updatedTasks = [...taskItems]; 110 | updatedTasks[editIndex].text = editTask; 111 | setTaskItems(updatedTasks); 112 | setModalVisible(false); 113 | setEditIndex(null); 114 | setErrorMessage(null); 115 | } 116 | } else { 117 | setErrorMessage("Content cannot be empty !"); 118 | startShakeAnimation(); 119 | } 120 | }; 121 | 122 | // 取消编辑任务的处理函数 123 | const handleCancelEdit = () => { 124 | setModalVisible(false); 125 | setEditIndex(null); 126 | setErrorMessage(null); 127 | }; 128 | 129 | // 完成任务的处理函数 130 | const completeTask = (index: number) => { 131 | let itemsCopy = [...taskItems]; // 复制任务列表 132 | itemsCopy.splice(index, 1); // 删除指定任务 133 | setTaskItems(itemsCopy); // 更新任务列表 134 | }; 135 | 136 | return ( 137 | 141 | 142 | {/* 添加 ScrollView 以便列表项多于页面时可以滚动 */} 143 | 149 | Today's tasks 150 | {/* 今日任务 */} 151 | 155 | setTask(text)} 160 | /> 161 | handleAddTask()}> 162 | 163 | + 164 | 165 | 166 | 167 | 168 | {taskItems.map((item, index) => ( 169 | completeTask(index)} 174 | onPress={() => handleEditTask(index)} 175 | /> 176 | ))} 177 | 178 | 179 | 180 | {/* 编辑任务模态框 */} 181 | 182 | 183 | 195 | Edit Task 196 | 202 | {errorMessage && ( 203 | {errorMessage} 204 | )} 205 | 206 | 86 | 87 | 88 | 89 | 90 | ); 91 | }; 92 | 93 | const styles = StyleSheet.create({ 94 | background: { 95 | flex: 1, 96 | justifyContent: "center", 97 | alignItems: "center", 98 | }, 99 | blurContainer: { 100 | flex: 1, 101 | justifyContent: "center", 102 | alignItems: "center", 103 | width: "100%", 104 | height: "100%", 105 | }, 106 | quoteContainer: { 107 | backgroundColor: "rgba(250, 250, 250, 0.6)", 108 | borderRadius: 50, 109 | margin: 15, 110 | padding: 20, // 添加 padding 以使文字与边框之间有空隙 111 | alignItems: "center", // 使内容居中对齐 112 | }, 113 | author: { 114 | marginTop: 10, 115 | fontSize: 16, 116 | fontStyle: "italic", 117 | color: "rgba(36, 59, 85, 0.8)", 118 | }, 119 | }); 120 | 121 | export default Welcome; 122 | -------------------------------------------------------------------------------- /app/+html.tsx: -------------------------------------------------------------------------------- 1 | import { ScrollViewStyleReset } from 'expo-router/html'; 2 | 3 | // This file is web-only and used to configure the root HTML for every 4 | // web page during static rendering. 5 | // The contents of this function only run in Node.js environments and 6 | // do not have access to the DOM or browser APIs. 7 | export default function Root({ children }: { children: React.ReactNode }) { 8 | return ( 9 | 10 | 11 | 12 | 13 | 14 | 15 | {/* 16 | Disable body scrolling on web. This makes ScrollView components work closer to how they do on native. 17 | However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line. 18 | */} 19 | 20 | 21 | {/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */} 22 |