From fe0e45643a9c8e3e9120bb53da990d3355471adb Mon Sep 17 00:00:00 2001 From: vyckey Date: Sat, 19 Oct 2024 09:01:18 +0800 Subject: [PATCH 01/23] add 404.html --- .github/workflows/node.js.yml | 2 +- public/404.html | 50 +++++++++++++++++++++++++++++++++++ public/index.html | 30 +++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 public/404.html diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 894879c..751f180 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -5,7 +5,7 @@ name: build & deploy vyckey.github.io to GitHub Pages on: push: - branches: [ "main"] + branches: [ "main", "test"] pull_request: branches: [ "main" ] diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000..758963b --- /dev/null +++ b/public/404.html @@ -0,0 +1,50 @@ + + + + + Single Page Apps for GitHub Pages + + + + diff --git a/public/index.html b/public/index.html index 8dbf801..fda9f96 100644 --- a/public/index.html +++ b/public/index.html @@ -23,6 +23,36 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> + + + React App From 91397f93796eea33d712d1d93ea3052a0b49dd11 Mon Sep 17 00:00:00 2001 From: vyckey Date: Sat, 19 Oct 2024 11:24:34 +0800 Subject: [PATCH 02/23] add timestamp tool --- package-lock.json | 23 +++ package.json | 2 + src/components/tools/Timestamp.tsx | 224 +++++++++++++++++++++++++++++ src/pages/ToolsLayout.tsx | 11 +- src/router/Router.tsx | 5 + 5 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 src/components/tools/Timestamp.tsx diff --git a/package-lock.json b/package-lock.json index 8978873..c014ac5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,8 @@ "@testing-library/user-event": "^13.5.0", "antd": "^5.21.3", "crypto-js": "^4.2.0", + "moment": "^2.30.1", + "moment-timezone": "^0.5.46", "react": "^18.3.1", "react-dom": "^18.3.1", "react-markdown": "^8.0.7", @@ -12826,6 +12828,27 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://mirrors.cloud.tencent.com/npm/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.46", + "resolved": "https://mirrors.cloud.tencent.com/npm/moment-timezone/-/moment-timezone-0.5.46.tgz", + "integrity": "sha512-ZXm9b36esbe7OmdABqIWJuBBiLLwAjrN7CE+7sYdCCx82Nabt1wHDj8TVseS59QIlfFPbOoiBPm6ca9BioG4hw==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmmirror.com/mri/-/mri-1.2.0.tgz", diff --git a/package.json b/package.json index caf4aa6..ade9918 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,8 @@ "@testing-library/user-event": "^13.5.0", "antd": "^5.21.3", "crypto-js": "^4.2.0", + "moment": "^2.30.1", + "moment-timezone": "^0.5.46", "react": "^18.3.1", "react-dom": "^18.3.1", "react-markdown": "^8.0.7", diff --git a/src/components/tools/Timestamp.tsx b/src/components/tools/Timestamp.tsx new file mode 100644 index 0000000..a0f387f --- /dev/null +++ b/src/components/tools/Timestamp.tsx @@ -0,0 +1,224 @@ +import { + Button, + Card, + Form, + Input, + InputNumber, + message, + Select, + Space, +} from 'antd'; +import moment from 'moment-timezone'; +import React, { useEffect, useRef, useState } from 'react'; +import { ClockCircleOutlined, CalendarOutlined } from '@ant-design/icons'; + +const Timestamp: React.FC = () => { + const [nowTime, setNowTime] = useState(moment()); + const [isRunning, setIsRunning] = useState(true); + const [isSeconds, setIsSeconds] = useState(false); + const timerRef = useRef(null); + + useEffect(() => { + const timer = setInterval(() => { + if (isRunning) { + setNowTime(moment()); + } + }, 1000); + timerRef.current = timer; + + return () => { + clearInterval(timer); + }; + }, [isRunning]); + + return ( + + + {isSeconds ? '秒' : '毫秒'} +

{nowTime.format('YYYY-MM-DD HH:mm:ss')}

+ + + + +
+ ); +}; + +interface TimeConvertFormData { + timestamp: number; + type: string; + timezone: string; +} + +interface DateConvertFormData { + date: string; + type: string; + timezone: string; +} + +const TimeConverter: React.FC = () => { + const [dateValue, setDateValue] = useState(''); + const [timeValue, setTimeValue] = useState(null); + + function convertTimeToDate(data: TimeConvertFormData) { + const ts = data.type === 'millis' ? data.timestamp : data.timestamp * 1000; + const date = moment.tz(ts, data.timezone).format('YYYY-MM-DD HH:mm:ss'); + setDateValue(date); + } + + function convertDateToTime(data: DateConvertFormData) { + const date = moment + .parseZone(data.date, 'YYYY-MM-DD HH:mm:ss', true) + .tz(data.timezone); + setTimeValue( + data.type === 'millis' ? date.valueOf() : Math.ceil(date.valueOf() / 1000) + ); + } + + return ( + <> + +

+ 时间戳转日期时间 +

+ + layout="inline" + initialValues={{ + timestamp: moment.tz('Asia/Shanghai').valueOf(), + type: 'millis', + timezone: 'Asia/Shanghai', + }} + onFinish={convertTimeToDate}> + + + + + ({ + label: name, + value: name, + }))} + /> + + + + + + + + + +

+ 日期时间转时间戳 +

+ + layout="inline" + initialValues={{ + date: moment.tz('Asia/Shanghai').format('YYYY-MM-DD HH:mm:ss'), + type: 'millis', + timezone: 'Asia/Shanghai', + }} + onFinish={convertDateToTime}> + + + + + ({ + label: name, + value: name, + }))} + /> + + + + + + + + +
+ + ); +}; + +const TimestampPanel: React.FC = () => { + return ( + <> + + + + ); +}; + +export default TimestampPanel; diff --git a/src/pages/ToolsLayout.tsx b/src/pages/ToolsLayout.tsx index 0cb02cd..05312c8 100644 --- a/src/pages/ToolsLayout.tsx +++ b/src/pages/ToolsLayout.tsx @@ -1,5 +1,9 @@ import React from 'react'; -import { LockOutlined, QrcodeOutlined } from '@ant-design/icons'; +import { + ClockCircleOutlined, + LockOutlined, + QrcodeOutlined, +} from '@ant-design/icons'; import { Layout, Menu, MenuProps, theme } from 'antd'; // import '../layout.css'; import { Outlet, useNavigate } from 'react-router-dom'; @@ -9,6 +13,11 @@ const { Header, Content, Footer } = Layout; type MenuItem = Required['items'][number]; const topItems: MenuItem[] = [ + { + key: 'timestamp', + label: '时间戳', + icon: , + }, { key: 'encryption', label: '加密/解密', diff --git a/src/router/Router.tsx b/src/router/Router.tsx index 2b0dcca..2ef59ff 100644 --- a/src/router/Router.tsx +++ b/src/router/Router.tsx @@ -4,6 +4,7 @@ import Homepage from '../pages/Homepage'; import ToolsLayout from '../pages/ToolsLayout'; import EncryptionTabs from '../components/tools/Encryption'; import QRCodeGenerator from '../components/tools/QRCodeGenerator'; +import TimestampPanel from '../components/tools/Timestamp'; const router = createBrowserRouter([ { @@ -18,6 +19,10 @@ const router = createBrowserRouter([ path: 'tools', element: , children: [ + { + path: 'timestamp', + element: , + }, { path: 'encryption', element: , From a75dbdb7551827b25f2f05b18ad43b591a25e8c0 Mon Sep 17 00:00:00 2001 From: vyckey Date: Sat, 19 Oct 2024 20:40:02 +0800 Subject: [PATCH 03/23] add time tools --- src/components/tools/TextDiffView.tsx | 7 + src/components/tools/TimerPanel.tsx | 190 ++++++++++++++++++++++++++ src/components/tools/Timestamp.tsx | 36 ++--- src/pages/ToolsLayout.tsx | 11 ++ src/router/Router.tsx | 10 ++ 5 files changed, 230 insertions(+), 24 deletions(-) create mode 100644 src/components/tools/TextDiffView.tsx create mode 100644 src/components/tools/TimerPanel.tsx diff --git a/src/components/tools/TextDiffView.tsx b/src/components/tools/TextDiffView.tsx new file mode 100644 index 0000000..b3668a4 --- /dev/null +++ b/src/components/tools/TextDiffView.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +const TextDiffView = () => { + return <>; +}; + +export default TextDiffView; diff --git a/src/components/tools/TimerPanel.tsx b/src/components/tools/TimerPanel.tsx new file mode 100644 index 0000000..352c3e0 --- /dev/null +++ b/src/components/tools/TimerPanel.tsx @@ -0,0 +1,190 @@ +import { Button, Card, DatePicker, Form } from 'antd'; +import dayjs from 'dayjs'; +import moment from 'moment'; +import React, { useRef, useState } from 'react'; + +interface DurationTextProps { + duration: moment.Duration; +} + +const DurationText: React.FC = ({ duration }) => { + return ( +
+ + {duration.years()}年{' '} + + + {duration.months()}月{' '} + + + {duration.days()}日{' '} + + + + {duration.hours()} 时 + + + {duration.minutes()} 分{' '} + + + {duration.seconds()}{' '} + 秒 + +
+ ); +}; + +const CountDownTimer = () => { + const [duration, setDuration] = useState(); + const timerRef = useRef(); + + function countDownTime(endTime: moment.Moment) { + if (timerRef.current) { + clearInterval(timerRef.current); + } + timerRef.current = setInterval(() => { + const duration = moment.duration(endTime.diff(moment())); + if (duration.asMilliseconds() < 0) { + setDuration(moment.duration(0)); + if (timerRef.current) { + clearInterval(timerRef.current); + } + } else { + setDuration(duration); + } + }, 1000); + } + + return ( + +
{ + const endTime = moment(values.time.valueOf()); + countDownTime(endTime); + return () => { + if (timerRef.current) { + clearInterval(timerRef.current); + } + }; + }}> + + + + + + + + + + + {!duration || duration.asMilliseconds() <= 0 ? ( + '已结束!' + ) : ( + + )} + +
+
+ ); +}; + +const CountTimer = () => { + const [duration, setDuration] = useState(); + const timerRef = useRef(); + + function countTime(startTime: moment.Moment) { + if (timerRef.current) { + clearInterval(timerRef.current); + } + timerRef.current = setInterval(() => { + const duration = moment.duration(moment().diff(startTime)); + if (duration.asMilliseconds() < 0) { + setDuration(moment.duration(0)); + if (timerRef.current) { + clearInterval(timerRef.current); + } + } else { + setDuration(duration); + } + }, 1000); + } + + return ( + +
{ + const startTime = moment(values.time.valueOf()); + countTime(startTime); + return () => { + if (timerRef.current) { + clearInterval(timerRef.current); + } + }; + }}> + + + + + + + + + + + {duration && } + +
+
+ ); +}; + +const TimerPanel = () => { + return ( + <> + + + + ); +}; + +export default TimerPanel; diff --git a/src/components/tools/Timestamp.tsx b/src/components/tools/Timestamp.tsx index a0f387f..3a502d3 100644 --- a/src/components/tools/Timestamp.tsx +++ b/src/components/tools/Timestamp.tsx @@ -81,6 +81,16 @@ interface DateConvertFormData { const TimeConverter: React.FC = () => { const [dateValue, setDateValue] = useState(''); const [timeValue, setTimeValue] = useState(null); + const timeOptions = [ + { + value: 'millis', + label: '毫秒(ms)', + }, + { + value: 'second', + label: '秒(s)', + }, + ]; function convertTimeToDate(data: TimeConvertFormData) { const ts = data.type === 'millis' ? data.timestamp : data.timestamp * 1000; @@ -122,18 +132,7 @@ const TimeConverter: React.FC = () => { - - ({ + label: '主题 ' + theme, + value: theme, + }))} + style={{ width: 160, textAlign: 'left' }} + onChange={value => setTheme(value)} + /> + ({ diff --git a/src/components/tools/PasswordGenerator.tsx b/src/components/tools/PasswordGenerator.tsx new file mode 100644 index 0000000..40a2c22 --- /dev/null +++ b/src/components/tools/PasswordGenerator.tsx @@ -0,0 +1,5 @@ +import React from 'react'; + +export default function PasswordGenerator() { + return <>密码生成器; +} diff --git a/src/components/tools/TimerPanel.tsx b/src/components/tools/TimerPanel.tsx index 352c3e0..88591e5 100644 --- a/src/components/tools/TimerPanel.tsx +++ b/src/components/tools/TimerPanel.tsx @@ -1,4 +1,4 @@ -import { Button, Card, DatePicker, Form } from 'antd'; +import { Button, Card, DatePicker, Form, Space } from 'antd'; import dayjs from 'dayjs'; import moment from 'moment'; import React, { useRef, useState } from 'react'; @@ -180,10 +180,10 @@ const CountTimer = () => { const TimerPanel = () => { return ( - <> + - + ); }; diff --git a/src/components/tools/Timestamp.tsx b/src/components/tools/Timestamp.tsx index 3a502d3..2492e98 100644 --- a/src/components/tools/Timestamp.tsx +++ b/src/components/tools/Timestamp.tsx @@ -32,11 +32,11 @@ const Timestamp: React.FC = () => { }, [isRunning]); return ( - + {isSeconds ? '秒' : '毫秒'} -

{nowTime.format('YYYY-MM-DD HH:mm:ss')}

+

{nowTime.format('YYYY-MM-DD HH:mm:ss')}