├── .DS_Store ├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── public ├── banner.png ├── demo1.jpg ├── demo2.jpg ├── demo3.jpg ├── demo4.jpg ├── index.html ├── logo128.png ├── manifest.json ├── news.js └── robots.txt └── src ├── App.css ├── App.js ├── AppIcons ├── Bilibili.svg ├── Bytedance.svg ├── Douban.svg ├── Douyin.svg ├── Github.svg ├── Juejin.svg ├── Leetcode.svg ├── NewTabIcon.svg ├── Toutiao.svg ├── Web.svg ├── Weibo.svg └── Xigua.svg ├── asset ├── Tomato.png ├── astronaut.json ├── motorbike.json ├── rest.mp3 ├── solar-system.json ├── solarsystem.json ├── tick.mp3 ├── woodenfish.mp3 ├── woodenfish.png └── work.mp3 ├── components ├── Account │ ├── Account.css │ └── Account.js ├── Apps │ ├── AppFolder │ │ ├── AppFolder.css │ │ └── AppFolder.js │ ├── Apps.css │ ├── Apps.js │ └── SetApp │ │ ├── SetApp.css │ │ └── SetApp.js ├── Calendar │ ├── CalComponent.css │ └── CalComponent.js ├── ChatRoom │ ├── ChatRoom.css │ └── ChatRoom.js ├── ClickMenu │ ├── ClickMenu.css │ └── ClickMenu.js ├── Clock │ ├── Clock.css │ ├── Clock.js │ └── SetClock │ │ ├── SetClock.css │ │ └── SetClock.js ├── Competition │ ├── Competition.css │ └── Competition.js ├── CountDown │ ├── CountDown.css │ └── CountDown.js ├── Demos │ ├── Demos.css │ └── Demos.js ├── FormHabit │ └── FormHabit.js ├── FuncCard │ ├── FuncCard.css │ ├── FuncCard.js │ └── SetFuncCard │ │ ├── SetFuncCard.css │ │ └── SetFuncCard.js ├── FuncModal │ ├── FuncModal.css │ └── FuncModal.js ├── FunctionAera │ ├── FunctionAera.css │ ├── FunctionAera.js │ └── SetFunctionAera │ │ ├── SetFunctionArea.css │ │ └── SetFunctionArea.js ├── MottoFooter │ ├── MottoFooter.css │ └── MottoFooter.js ├── News │ ├── News.css │ └── News.js ├── Notes │ ├── index.css │ ├── index.js │ ├── markdown-notes │ │ ├── index.css │ │ └── index.js │ └── noesTabs.js ├── Pictures │ ├── index.js │ └── style.css ├── Search │ ├── Search.css │ └── Search.js ├── ServerMonitor │ ├── ServerMonitor.css │ └── ServerMonitor.js ├── SetBackground │ ├── SetBackground.css │ └── SetBackground.js ├── Setting │ └── Setting.js ├── Snippets │ ├── Snippets.css │ └── Snippets.js ├── StockMarket │ ├── StockMarket.css │ └── StockMarket.js ├── SwiperAera │ ├── SwiperAera.css │ └── SwiperAera.js ├── Todo │ ├── AddItemInput │ │ ├── index.css │ │ └── index.js │ ├── EditItemInput.js │ ├── Todo.js │ ├── TodoItems │ │ ├── index.css │ │ └── index.js │ ├── TodoModal.css │ └── TodoModal.js ├── TomatoClock │ ├── TomatoClock.css │ └── TomatoClock.js ├── ToolKit │ ├── ToolKit.css │ └── ToolKit.js ├── TopNav │ ├── TopNav.css │ └── TopNav.js ├── Weather │ ├── Weather.js │ ├── plugin.js │ └── style.css └── WoodenFish │ ├── WoodenFish.css │ └── WoodenFish.js ├── config └── index.js ├── font ├── DS-Digital Bold.ttf ├── UnidreamLED.eot ├── UnidreamLED.woff ├── iconfont.css ├── iconfont.js ├── iconfont.json └── iconfont.ttf ├── hooks ├── useLocalStorage.js └── useScript.js ├── index.css ├── index.js ├── logo.svg ├── reportWebVitals.js ├── service └── getCookie.js ├── setupProxy.js ├── setupTests.js └── store ├── index.js └── reducer.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/.DS_Store -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | # VScode需要安装插件支持 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | 16 | [Makefile] 17 | indent_style = tab 18 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/.eslintignore -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": ["eslint:recommended", "plugin:react/recommended"], 7 | "overrides": [], 8 | "parserOptions": { 9 | "ecmaVersion": "latest", 10 | "sourceType": "module" 11 | }, 12 | "plugins": ["react"], 13 | "rules": { 14 | "quotes": 1, 15 | "semi": 1, 16 | "no-console": 1, 17 | "no-undef": "off", 18 | "no-restricted-globals": "off", 19 | "no-unused-vars": 1, 20 | "react/react-in-jsx-scope":"off", 21 | "react/prop-types":"off", 22 | "react/display-name": 1, 23 | "react/jsx-key": 1 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Build app and deploy to Aliyun 4 | # 将CI设置为false,不然会把warning当error 5 | env: 6 | CI: false 7 | on: 8 | #监听push操作 9 | push: 10 | branches: 11 | # mian分支,你也可以改成其他分支 12 | - main 13 | jobs: 14 | # 任务ID 15 | build: 16 | # 运行环境 17 | runs-on: ubuntu-latest 18 | # 步骤 19 | steps: 20 | # 使用别人的action 21 | - uses: actions/checkout@v2 22 | # 步骤名称 23 | - name: npm install 24 | # 步骤执行指令 25 | run: npm install -legacy-peer-deps 26 | - name: npm run build 27 | run: npm run build 28 | # 命名这个任务为发布Deploy 29 | - name: Deploy 30 | # 因为构建之后,需要把代码上传到服务器上,所以需要连接到ssh,并且做一个拷贝操作 31 | uses: appleboy/scp-action@v0.1.7 32 | env: 33 | WELCOME: "ssh scp ssh pipelines" 34 | LASTSSH: "Doing something after copying" 35 | with: 36 | host: ${{ secrets.USER_HOST }} 37 | username: ${{ secrets.USER_NAME }} 38 | # pass: ${{ secrets.USER_PASS }} 39 | key: ${{ secrets.USER_KEY }} 40 | source: "./build/*" 41 | target: /home/www/newtab.wisdompanda 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | #scripts 26 | *.sh 27 | *.py 28 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "bracketSameLine": false, 4 | "bracketSpacing": true, 5 | "embeddedLanguageFormatting": "auto", 6 | "htmlWhitespaceSensitivity": "css", 7 | "insertPragma": false, 8 | "jsxSingleQuote": true, 9 | "printWidth": 80, 10 | "proseWrap": "preserve", 11 | "quoteProps": "as-needed", 12 | "requirePragma": false, 13 | "semi": true, 14 | "singleQuote": false, 15 | "tabWidth": 2, 16 | "trailingComma": "es5", 17 | "useTabs": false, 18 | "vueIndentScriptAndStyle": false 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 wisdompandamaster 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "newtab", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@ant-design/icons": "^4.7.0", 7 | "@ant-design/pro-components": "^1.1.3", 8 | "@ant-design/pro-form": "^1.69.1", 9 | "@ant-design/pro-table": "^2.76.1", 10 | "@ant-design/pro-utils": "^1.42.0", 11 | "@babel/runtime": "^7.18.3", 12 | "@testing-library/jest-dom": "^5.16.2", 13 | "@testing-library/react": "^12.1.2", 14 | "@testing-library/user-event": "^13.5.0", 15 | "antd": "^4.18.6", 16 | "array-move": "^4.0.0", 17 | "axios": "^0.26.1", 18 | "babel": "^6.23.0", 19 | "braft-editor": "^2.3.9", 20 | "braft-extensions": "^0.1.1", 21 | "canvas-confetti": "^1.6.0", 22 | "draft-js-prism": "^1.0.6", 23 | "fetch-jsonp": "^1.2.1", 24 | "highlight.js": "^11.4.0", 25 | "immutability-helper": "^3.1.1", 26 | "js-md5": "^0.7.3", 27 | "jsonp": "^0.2.1", 28 | "lunar-javascript": "^1.2.37", 29 | "markdown-it": "^12.3.2", 30 | "memfs": "^4.2.0", 31 | "minio": "^7.0.26", 32 | "prismjs": "^1.29.0", 33 | "react": "^17.0.2", 34 | "react-activity-calendar": "^1.6.0", 35 | "react-calendar": "^3.7.0", 36 | "react-cookies": "^0.1.1", 37 | "react-datepicker": "^4.6.0", 38 | "react-dnd": "^15.1.1", 39 | "react-dnd-html5-backend": "^15.1.2", 40 | "react-dom": "^17.0.2", 41 | "react-draggable": "^4.4.5", 42 | "react-github-calendar": "^3.3.1", 43 | "react-lottie": "^1.2.3", 44 | "react-markdown-editor-lite": "^1.3.2", 45 | "react-scripts": "5.0.0", 46 | "react-sortable-hoc": "^2.0.0", 47 | "swiper": "^8.1.6", 48 | "web-vitals": "^2.1.4" 49 | }, 50 | "scripts": { 51 | "start": "react-scripts start", 52 | "build": "CI=false && react-scripts build", 53 | "test": "react-scripts test", 54 | "eject": "react-scripts eject" 55 | }, 56 | "eslintConfig": { 57 | "extends": [ 58 | "react-app", 59 | "react-app/jest" 60 | ] 61 | }, 62 | "browserslist": { 63 | "production": [ 64 | ">0.2%", 65 | "not dead", 66 | "not op_mini all" 67 | ], 68 | "development": [ 69 | "last 1 chrome version", 70 | "last 1 firefox version", 71 | "last 1 safari version" 72 | ] 73 | }, 74 | "devDependencies": { 75 | "eslint": "^8.26.0", 76 | "eslint-plugin-react": "^7.31.10", 77 | "prettier": "^2.7.1", 78 | "react-redux": "^7.2.6", 79 | "redux": "^4.1.2" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /public/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/public/banner.png -------------------------------------------------------------------------------- /public/demo1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/public/demo1.jpg -------------------------------------------------------------------------------- /public/demo2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/public/demo2.jpg -------------------------------------------------------------------------------- /public/demo3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/public/demo3.jpg -------------------------------------------------------------------------------- /public/demo4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/public/demo4.jpg -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 21 | 22 | 31 | NewTab 标签页 32 | 33 | 34 | 35 |
36 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /public/logo128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/public/logo128.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo512.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | @import "~antd/dist/antd.css"; 2 | 3 | /* 全局CSS样式 */ 4 | 5 | :root { 6 | /* 定义CSS变量,之后设置简单模式,浅色或深色模式的时候可以用*/ 7 | --dark-color: #0000004d; 8 | } 9 | body { 10 | line-height: normal; 11 | min-width: 800px; 12 | } 13 | .App { 14 | position: absolute; 15 | width: 100vw; 16 | height: 100vh; 17 | /* background-color: #59887c4d; */ 18 | } 19 | .mask { 20 | width: 100vw; 21 | height: 100vh; 22 | position: fixed; 23 | background-color: #000; 24 | } 25 | .background { 26 | width: 100vw; 27 | height: 100vh; 28 | position: fixed; 29 | transition: background-image 0.3s ease-in-out; 30 | /*通过缩放来去掉白边 */ 31 | } 32 | .clockSearch { 33 | display: flex; 34 | justify-content: center; 35 | } 36 | 37 | /* -------------更改的antd modal组件样式--------- */ 38 | .ant-modal-header { 39 | border-radius: 10px; 40 | } 41 | .ant-modal-content { 42 | border-radius: 10px; 43 | } 44 | .ant-modal-close-x:hover { 45 | background-color: #0000004d; 46 | border-radius: 0 10px 0 0; 47 | } 48 | .ant-collapse > .ant-collapse-item:last-child, 49 | .ant-collapse-borderless > .ant-collapse-item:last-child .ant-collapse-header { 50 | border-radius: 10px; 51 | } 52 | 53 | /* Todo组件的css */ 54 | #todo-app { 55 | /* position: absolute; */ 56 | display: inline-block; 57 | /* width: 18vw; 58 | height: 8.5vw; */ 59 | width: 352px; 60 | height: 165px; 61 | /* left: 392px; 62 | top: 20px; */ 63 | /* left:20px; 64 | top:205px; */ 65 | padding-bottom: 10px; 66 | /* font-family: ; */ 67 | padding-top: 10px; 68 | background: #ffffff; 69 | border: 0px solid #ffffff; 70 | border-radius: 20px; 71 | transition: 0.1s ease-in; 72 | } 73 | #todo-app:active { 74 | transform: scale(0.95); 75 | } 76 | 77 | #todo-container { 78 | display: flex; 79 | height: 100%; 80 | max-height: 165px; 81 | border-radius: 20px; 82 | justify-content: space-between; 83 | } 84 | 85 | #side-container { 86 | width: 40%; 87 | border-radius: 20px; 88 | } 89 | 90 | #todo-header { 91 | height: 20%; 92 | border-radius: 20px; 93 | } 94 | 95 | #app-icon { 96 | position: relative; 97 | width: 6px; 98 | height: 14px; 99 | left: 18px; 100 | top: 12px; 101 | background: linear-gradient(180deg, #b1ff64 0%, #11f2af 100%); 102 | box-shadow: 0px 3px 6px rgba(71, 255, 55, 0.8); 103 | border-radius: 20px; 104 | } 105 | 106 | /* #app-title { 107 | position: relative; 108 | width: 100px; 109 | left: 36px; 110 | top:-7px; 111 | font-style: normal; 112 | font-weight: bold; 113 | font-size: 18px; 114 | line-height: 22px; 115 | letter-spacing: 0.2rem; 116 | color: rgba(0, 0, 0, 0.85); 117 | cursor: pointer; 118 | } */ 119 | 120 | #todo-count { 121 | height: 90%; 122 | border-radius: 20px; 123 | display: flex; 124 | align-items: center; 125 | justify-content: space-evenly; 126 | cursor: pointer; 127 | } 128 | 129 | .count { 130 | display: flex; 131 | flex-direction: column; 132 | align-items: center; 133 | justify-content: space-evenly; 134 | } 135 | 136 | .count h1 { 137 | font-style: normal; 138 | font-weight: 400; 139 | font-size: 35px; 140 | line-height: 35px; 141 | } 142 | 143 | #count-completed { 144 | color: #63ff3c; 145 | } 146 | 147 | .count p { 148 | font-style: normal; 149 | font-weight: 500; 150 | font-size: 15px; 151 | line-height: 20px; 152 | letter-spacing: 0.05em; 153 | } 154 | 155 | #p-completed { 156 | color: rgba(0, 0, 0, 0.5); 157 | } 158 | 159 | #items-container { 160 | width: 60%; 161 | height: 145px; 162 | display: flex; 163 | margin-top: -25px; 164 | flex-direction: column; 165 | overflow-x: hidden; 166 | overflow-y: scroll; 167 | justify-items: space-evenly; 168 | } 169 | 170 | #items-container::-webkit-scrollbar { 171 | width: 0; 172 | height: 0; 173 | color: transparent; 174 | } 175 | 176 | .todo-item { 177 | display: flex; 178 | cursor: pointer; 179 | user-select: none; 180 | font-size: 18px; 181 | } 182 | 183 | .item-icon { 184 | height: 1em; 185 | width: 1em; 186 | margin-top: 0.1em; 187 | margin-right: 1em; 188 | } 189 | 190 | .item-text { 191 | width: 100%; 192 | font-style: normal; 193 | font-weight: normal; 194 | font-size: 18px; 195 | line-height: 18px; 196 | margin-right: 1em; 197 | font-weight: 600; 198 | white-space: nowrap; 199 | overflow: hidden; 200 | text-overflow: ellipsis; 201 | } 202 | 203 | .item-completed { 204 | /* text-decoration: line-through; */ 205 | opacity: 0.2; 206 | } 207 | 208 | #items-completed-header { 209 | color: gray; 210 | font-size: 1em; 211 | font-weight: 400; 212 | } 213 | 214 | hr { 215 | width: 90%; 216 | height: 0px; 217 | border: 0.5px solid rgba(0, 0, 0, 0.1); 218 | margin: 0.3em 0; 219 | } 220 | 221 | #modal-button { 222 | border: 0; 223 | background-color: #ffffff; 224 | color: gray; 225 | text-align: right; 226 | position: absolute; 227 | right: 10px; 228 | height: 24px; 229 | top: 10px; 230 | text-decoration: underline; 231 | margin-right: 1em; 232 | margin-top: 0em; 233 | } 234 | 235 | #modal-button:hover { 236 | color: #63ff3c; 237 | } 238 | 239 | .ant-modal { 240 | padding-bottom: 0; 241 | max-width: 100vw; 242 | } 243 | 244 | .ant-modal-content { 245 | border-radius: 10px; 246 | } 247 | 248 | .ant-modal-header { 249 | border-radius: 10px; 250 | } 251 | 252 | .ant-modal-body { 253 | border-radius: 10px; 254 | overflow: scroll; 255 | padding: 1.5% 1.5% 1.5% 1.5%; 256 | } 257 | 258 | .ant-modal-body::-webkit-scrollbar { 259 | width: 0; 260 | height: 0; 261 | color: transparent; 262 | } 263 | 264 | .ant-modal-footer { 265 | display: none; 266 | } 267 | 268 | #items-completed-header { 269 | height: 2em; 270 | text-align: center; 271 | } 272 | 273 | #items-completed-header h1 { 274 | color: gray; 275 | font-size: 1em; 276 | font-weight: 400; 277 | } 278 | 279 | hr.todo-hr { 280 | width: auto; 281 | border: 0.5px solid rgb(211, 211, 211); 282 | } 283 | 284 | .react-datepicker__input-container { 285 | top: -11px; 286 | } 287 | 288 | /* 天气组件的CSS */ 289 | #Weather { 290 | position: absolute; 291 | width: 352px; 292 | height: 165px; 293 | left: 183px; 294 | top: 390px; 295 | background: #ffffff; 296 | border: 1px solid #ffffff; 297 | border-radius: 20px; 298 | } 299 | 300 | .snippets-container { 301 | position: absolute; 302 | z-index: 2; 303 | top: 100px; 304 | left: 200px; 305 | } 306 | 307 | /* 设置新闻里的列表背景颜色 */ 308 | .ant-tabs-content-holder { 309 | background-color: rgba(255, 255, 255, 0.8); 310 | } 311 | -------------------------------------------------------------------------------- /src/AppIcons/Bilibili.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/AppIcons/Bytedance.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/AppIcons/Douban.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/AppIcons/Douyin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/AppIcons/Github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/AppIcons/Leetcode.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/AppIcons/NewTabIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /src/AppIcons/Toutiao.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/AppIcons/Web.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/AppIcons/Weibo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/AppIcons/Xigua.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/asset/Tomato.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/src/asset/Tomato.png -------------------------------------------------------------------------------- /src/asset/rest.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/src/asset/rest.mp3 -------------------------------------------------------------------------------- /src/asset/tick.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/src/asset/tick.mp3 -------------------------------------------------------------------------------- /src/asset/woodenfish.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/src/asset/woodenfish.mp3 -------------------------------------------------------------------------------- /src/asset/woodenfish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/src/asset/woodenfish.png -------------------------------------------------------------------------------- /src/asset/work.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/src/asset/work.mp3 -------------------------------------------------------------------------------- /src/components/Account/Account.css: -------------------------------------------------------------------------------- 1 | .login_form { 2 | display: flex; 3 | flex-wrap: wrap; 4 | width: 271px; 5 | margin: 0 auto; 6 | font-size: 14px; 7 | line-height: 20px; 8 | font-weight: 700; 9 | } 10 | .login_form label { 11 | width: 100%; 12 | margin: 5px auto; 13 | } 14 | .login_form input { 15 | display: block; 16 | width: 100%; 17 | height: 30px; 18 | margin-top: 5px; 19 | border: 1px solid #000000; 20 | box-sizing: border-box; 21 | border-radius: 10px; 22 | padding-left: 10px; 23 | line-height: 30px; 24 | outline: none; 25 | } 26 | .login_form input:nth-child(3) { 27 | background: linear-gradient(180deg, #6cdcff 0%, #3355ff 100%); 28 | box-shadow: 0px 4px 10px rgba(17, 84, 255, 0.6); 29 | border: none; 30 | margin-top: 20px; 31 | margin-bottom: 20px; 32 | letter-spacing: 0.5em; 33 | color: #ffffff; 34 | transition: 0.3s ease-in; 35 | cursor: pointer; 36 | } 37 | .login_form input:nth-child(3):active { 38 | transform: scale(0.9); 39 | } 40 | form.login_form + div { 41 | width: 274px; 42 | border: 1px solid rgba(0, 0, 0, 0.2); 43 | margin: 10px auto 0 auto; 44 | } 45 | .noaccount { 46 | position: relative; 47 | width: 120px; 48 | text-align: center; 49 | height: 17px; 50 | margin: 10px auto -15px auto; 51 | top: -20px; 52 | background-color: #ffffff; 53 | line-height: 17px; 54 | } 55 | 56 | .onsignin { 57 | display: flex; 58 | justify-content: space-around; 59 | align-items: center; 60 | font-size: 20px; 61 | line-height: 20px; 62 | font-weight: 700; 63 | } 64 | -------------------------------------------------------------------------------- /src/components/Account/Account.js: -------------------------------------------------------------------------------- 1 | import { Button, message } from "antd"; 2 | import { useState } from "react"; 3 | import "./Account.css"; 4 | import cookie from "react-cookies"; 5 | 6 | export default function Account() { 7 | const [username, setUsername] = useState(""); 8 | const [password, setPassword] = useState(""); 9 | 10 | const handleSubmit = e => { 11 | e.preventDefault(); 12 | let data = new FormData(); 13 | data.append("username", username); 14 | data.append("password", password); 15 | fetch("/api/account/signin/", { 16 | method: "post", 17 | body: data, 18 | credentials: "include", 19 | }) 20 | .then(function (res) { 21 | return res.json(); 22 | }) 23 | .then(function (data) { 24 | if (data.code === "200") { 25 | //message.success(data.msg) 26 | window.location.reload(); 27 | } else if (data.code === "201") { 28 | message.error(data.msg); 29 | } 30 | }); 31 | }; 32 | 33 | const onSignOut = e => { 34 | e.preventDefault(); 35 | fetch("/api/account/signout/", { 36 | method: "get", 37 | credentials: "include", 38 | }) 39 | .then(function (res) { 40 | return res.json(); 41 | }) 42 | .then(function (data) { 43 | // message.success(data.msg) 44 | localStorage.clear(); 45 | // cookie.remove('status') 46 | // console.log('remove cookie') 47 | // cookie.remove('username') 48 | window.location.reload(); 49 | }); 50 | }; 51 | 52 | if (cookie.load("status") === "200") { 53 | //如果已经登录 54 | return ( 55 |
56 | {cookie.load("username")} 57 | 60 |
61 | ); 62 | } else 63 | return ( 64 |
65 |
66 | 76 | 86 | 87 |
88 |
89 |
90 |
没有账户? 注册
91 |
92 |
93 | ); 94 | } 95 | -------------------------------------------------------------------------------- /src/components/Apps/AppFolder/AppFolder.css: -------------------------------------------------------------------------------- 1 | .app-folder { 2 | width: 4.5vw; 3 | height: 4.5vw; 4 | min-height: 60px; 5 | min-width: 60px; 6 | border: 2px solid white; 7 | background: rgba(255, 255, 255, 0.5); 8 | border-radius: 20px; 9 | transition: 0.5s; 10 | cursor: pointer; 11 | } 12 | .app-contents { 13 | width: 90%; 14 | height: 88%; 15 | margin: 6% auto 0 auto; 16 | display: grid; 17 | grid-template-columns: repeat(3, 1fr); 18 | grid-template-rows: repeat(3, 1fr); 19 | align-items: center; 20 | justify-items: center; 21 | } 22 | .app-contents img { 23 | width: 0.9vw; 24 | border-radius: 5px; 25 | } 26 | .app-folder-modal { 27 | display: grid; 28 | width: 100%; 29 | height: 100%; 30 | grid-template-columns: repeat(4, 1fr); 31 | grid-template-rows: repeat(4, 1fr); 32 | align-items: center; 33 | justify-items: center; 34 | } 35 | .app-folder-modal img { 36 | border-radius: 20px; 37 | } 38 | .folder-name { 39 | position: fixed; 40 | font-size: 2rem; 41 | letter-spacing: 0.5rem; 42 | font-weight: 600; 43 | top: -4rem; 44 | text-align: center; 45 | color: azure; 46 | width: 96%; 47 | user-select: none; 48 | cursor: pointer; 49 | } 50 | .folder-name-input { 51 | position: fixed; 52 | font-size: 2rem; 53 | letter-spacing: 0.5rem; 54 | font-weight: 600; 55 | top: -4rem; 56 | text-align: center; 57 | color: azure; 58 | width: 95%; 59 | border: 0; 60 | background: rgba(255, 255, 255, 0); 61 | outline: none; 62 | } 63 | .folder-name-input:focus { 64 | outline: none; 65 | /* border-bottom: 2px solid whitesmoke; */ 66 | box-shadow: 0px 2px 0px 0px rgba(255, 255, 255, 0.8); 67 | user-select: none; 68 | } 69 | -------------------------------------------------------------------------------- /src/components/Apps/AppFolder/AppFolder.js: -------------------------------------------------------------------------------- 1 | import FuncModal from "../../FuncModal/FuncModal"; 2 | import { memo, useEffect, useState } from "react"; 3 | import { useSelector, useDispatch } from "react-redux"; 4 | import "./AppFolder.css"; 5 | 6 | function AppFolder(props) { 7 | // modal组件控制函数 8 | const [isModalVisible, setIsModalVisible] = useState(false); 9 | const [ediable, setEdiable] = useState(false); 10 | const { contents, name, folderId } = props; 11 | const [nameinput, setNameInput] = useState(name); 12 | const myApps = useSelector(state => state.myApps); 13 | // 文件夹位置 14 | let folderIndex = myApps.findIndex(i => i.id === folderId); 15 | 16 | const dispatch = useDispatch(); 17 | 18 | useEffect(() => { 19 | // () => { 20 | // let node = document.querySelector("div.app-folder"); 21 | // node.style.transform = "scale(0)"; 22 | // }; 23 | }, []); 24 | 25 | const showModal = () => { 26 | // openNotification(); 27 | setIsModalVisible(true); 28 | }; 29 | const handleOk = () => { 30 | setIsModalVisible(false); 31 | }; 32 | const handleCancel = () => { 33 | setIsModalVisible(false); 34 | }; 35 | 36 | const onDragEnd = (e, item) => { 37 | e.stopPropagation(); 38 | e.preventDefault(); 39 | 40 | let foldernode = e.target.parentNode.parentNode.parentNode.parentNode; 41 | let region = foldernode.getBoundingClientRect(); 42 | if ( 43 | e.clientY >= region.top && 44 | e.clientY <= region.bottom && 45 | e.clientX >= region.left && 46 | e.clientX <= region.right 47 | ) { 48 | console.log("in"); 49 | } else { 50 | // 去除文件夹内图标 51 | myApps[folderIndex].children.splice( 52 | myApps[folderIndex].children.findIndex(i => i.id === item.id), 53 | 1 54 | ); 55 | 56 | // dispatch时就移除文件夹,没有动画 57 | if (myApps[folderIndex].children.length === 0) { 58 | // let node = document.querySelector("div.app-folder"); 59 | // node.style.transform = "scale(0)"; 60 | myApps.splice(folderIndex, 1); 61 | } 62 | // console.log(myApps); 63 | const apps = [...myApps, item]; 64 | dispatch({ 65 | type: "CHANGE_APPS", 66 | myApps: apps, 67 | }); 68 | localStorage.setItem("apps", JSON.stringify(apps)); 69 | } 70 | }; 71 | 72 | const OnDrag = (e, item) => { 73 | e.stopPropagation(); 74 | e.preventDefault(); 75 | let region = 76 | e.target.parentNode.parentNode.parentNode.parentNode.getBoundingClientRect(); 77 | if ( 78 | !( 79 | e.clientY >= region.top && 80 | e.clientY <= region.bottom && 81 | e.clientX >= region.left && 82 | e.clientX <= region.right 83 | ) 84 | ) { 85 | setIsModalVisible(false); 86 | } 87 | }; 88 | 89 | const editFolderName = (e, name) => { 90 | e.preventDefault(); 91 | e.stopPropagation(); 92 | setEdiable(true); 93 | }; 94 | 95 | const onKeyDown = e => { 96 | // console.log(e); 97 | if (e.key == "Enter") { 98 | setEdiable(false); 99 | 100 | myApps[folderIndex].name = nameinput || "文件夹"; 101 | dispatch({ 102 | type: "CHANGE_APPS", 103 | myApps: myApps, 104 | }); 105 | localStorage.setItem("apps", JSON.stringify(myApps)); 106 | // console.log(e); 107 | } 108 | }; 109 | const addFolder = () => {}; 110 | 111 | // document.addEventListener("dragenter", function (event) { 112 | // event.preventDefault(); 113 | // // console.log("enter"); 114 | // if (event.target.className == "app-folder") { 115 | // event.target.style.boxShadow = "0 0 0 4px #007ACC"; 116 | // } 117 | // }); 118 | 119 | // const dragEnterIn = () => { 120 | // console.log("drag enter"); 121 | // // if (e.target.className === "apps_sortableItem") { 122 | // // e.target.style.transform = "scale(1.2)"; 123 | // // } 124 | // }; 125 | 126 | return ( 127 | <> 128 |
133 |
134 | {contents.map((item, index) => ( 135 | {item.name} 136 | ))} 137 |
138 |
139 | 151 |
152 | {contents.map((item, index) => ( 153 | onDragEnd(e, item)} 159 | onDrag={e => OnDrag(e, item)} 160 | // onDrop={e => { 161 | // e.stopPropagation(); 162 | // console.log(e); 163 | // }} 164 | > 165 | {item.name} 171 | 172 | ))} 173 |
174 | setNameInput(e.target.value)} 180 | /> 181 |
editFolderName(e, name)} 185 | > 186 | {nameinput || "文件夹"} 187 |
188 |
189 | 190 | ); 191 | } 192 | 193 | export default memo(AppFolder); 194 | -------------------------------------------------------------------------------- /src/components/Apps/Apps.css: -------------------------------------------------------------------------------- 1 | .Apps { 2 | display: flex; 3 | flex-wrap: wrap; 4 | justify-content: center; 5 | border: 0px solid #000000; 6 | position: relative; 7 | /* z-index:999; */ 8 | bottom: 0vh; 9 | margin: 0 auto 20px auto; 10 | width: 84vw; 11 | /* height: 20vw; */ 12 | border-radius: 20px; 13 | overflow-y: scroll; 14 | overflow-x: hidden; 15 | font-size: small; 16 | text-align: center; 17 | background-color: #ffffff00; 18 | transition: 0.3s ease-in-out; 19 | } 20 | .apps_sortableItem a img:hover { 21 | box-shadow: 0 0 25px rgb(255, 255, 255), 0 0 10px rgb(255, 255, 255) inset; 22 | } 23 | 24 | .apps_sortableItem a { 25 | transition: 0.2s ease-out; 26 | position: relative; 27 | width: 60px; 28 | min-height: 60px; 29 | /* 这个transition挺重要,和事件执行有关,这个时间要比外面的慢,才先执行,待弄懂 */ 30 | } 31 | 32 | .apps_sortableItem a img { 33 | width: 4.5vw; 34 | min-width: 60px; 35 | max-width: 4.5vw; 36 | border-radius: 20px; 37 | transition: 0.2s ease-in-out; 38 | } 39 | .apps_sortable { 40 | display: grid; 41 | grid-template-columns: repeat(8, calc(100% / 8)); 42 | grid-template-rows: none; 43 | justify-content: center; 44 | justify-items: center; 45 | overflow-x: hidden; 46 | width: 70%; 47 | position: relative; 48 | } 49 | /* .delete{ 50 | transform: scale(0); 51 | transition:2s ease-in-out; 52 | } */ 53 | .apps_sortableItem { 54 | width: 100%; 55 | height: 100%; 56 | display: flex; 57 | align-items: center; 58 | position: relative; 59 | z-index: 2; 60 | } 61 | .shake { 62 | animation: icon_shake 0.2s infinite; 63 | } 64 | .apps_sortableItem button { 65 | position: relative; 66 | margin-top: -50%; 67 | left: 66%; 68 | z-index: 1; 69 | } 70 | /* .Apps:active { 71 | transform: scale(0.8); 72 | } */ 73 | @keyframes icon_shake { 74 | 0% { 75 | transform: rotate(3deg); 76 | } 77 | 25% { 78 | transform: rotate(0); 79 | } 80 | 50% { 81 | transform: rotate(-3deg); 82 | } 83 | 75% { 84 | transform: rotate(0deg); 85 | } 86 | 100% { 87 | transform: rotate(3deg); 88 | } 89 | } 90 | 91 | .apps_sortable::-webkit-scrollbar, 92 | .Apps::-webkit-scrollbar { 93 | width: 0px; 94 | } 95 | /* div::-webkit-scrollbar-thumb { 96 | border-radius: 10px; 97 | box-shadow: inset 0 0 4px #00000033; 98 | background: rgba(0,0,0,0.2); 99 | } 100 | div::-webkit-scrollbar-track { 101 | box-shadow: inset 0 0 5px rgba(0,0,0,0.2); 102 | border-radius: 10px; 103 | background: rgba(0,0,0,0.1); 104 | } */ 105 | -------------------------------------------------------------------------------- /src/components/Apps/Apps.js: -------------------------------------------------------------------------------- 1 | import "./Apps.css"; 2 | import "../../font/iconfont.css"; 3 | import { SortableContainer, SortableElement } from "react-sortable-hoc"; 4 | import { arrayMoveImmutable } from "array-move"; 5 | import { useSelector, useDispatch } from "react-redux"; 6 | import React, { useState, useEffect } from "react"; 7 | import { Button } from "antd"; 8 | import { CloseOutlined } from "@ant-design/icons"; 9 | import AppFolder from "./AppFolder/AppFolder"; 10 | // import ClickMenu from "../ClickMenu/ClickMenu"; 11 | 12 | export default function Apps() { 13 | const dispatch = useDispatch(); 14 | const deleteMode = useSelector(state => state.deleteApp); 15 | const myApps = useSelector(state => state.myApps); 16 | const [items, setItems] = useState(myApps); 17 | // let timePos = useSelector(state => state.timePos); 18 | 19 | let height = "30vw"; 20 | 21 | useEffect(() => { 22 | //这一行让Apps SetApps 拖拽时可以同步变换 23 | setItems(myApps); 24 | }, [myApps]); 25 | 26 | const deleteApp = id => { 27 | // e.target.parentNode.classList.add('delete') 28 | // console.log('deleteAPP') 29 | let updatemyApps = myApps.filter(item => item.id !== id); 30 | localStorage.setItem("apps", JSON.stringify(updatemyApps)); 31 | setItems(updatemyApps); 32 | dispatch({ 33 | type: "CHANGE_APPS", 34 | myApps: updatemyApps, 35 | }); 36 | }; 37 | 38 | const handleContextMenu = (e, b) => { 39 | e.preventDefault(); 40 | e.stopPropagation(); 41 | dispatch({ 42 | type: "CHANGE_DELETEAPP", 43 | deleteApp: !b, 44 | }); 45 | }; 46 | 47 | const renderItem = item => { 48 | const shake = deleteMode ? " shake" : ""; 49 | const app = ( 50 | 51 | handleContextMenu(e, deleteMode)} 53 | alt={item.name} 54 | src={item.imgPath} 55 | /> 56 | 57 | ); 58 | const folder = ( 59 |
60 | 65 |
66 | ); 67 | 68 | return ( 69 |
70 |
79 | ); 80 | }; 81 | 82 | //拖拽排序插件----------------------------------- 83 | const SortableItem = SortableElement(({ value }) => renderItem(value)); 84 | const SortableList = SortableContainer(({ items }) => { 85 | return ( 86 |
87 | {items.map((value, index) => ( 88 | 89 | ))} 90 |
91 | ); 92 | }); 93 | 94 | const onSortEnd = ({ oldIndex, newIndex }) => { 95 | setItems(arrayMoveImmutable(items, oldIndex, newIndex)); 96 | localStorage.setItem( 97 | "apps", 98 | JSON.stringify(arrayMoveImmutable(items, oldIndex, newIndex)) 99 | ); //这里更改了,但是还不能同步 100 | dispatch({ 101 | type: "CHANGE_APPS", 102 | myApps: arrayMoveImmutable(items, oldIndex, newIndex), 103 | }); 104 | }; 105 | 106 | // const onSortOver = ( 107 | // // { index, oldIndex, newIndex, collection, isKeySorting }, 108 | // e 109 | // ) => { 110 | // e.helper.querySelector("div").style.transform = "scale(0.8)"; 111 | // }; 112 | //------------------------------------------------- 113 | 114 | // const renderCard = (item)=>{ 115 | // return ( //因为click事件会引发拖动,所以这里click没有生效,设置transition解决了 116 | // {item.name} 117 | // ) 118 | // } 119 | 120 | return ( 121 | //
{myApps.map((item, i) => renderItem(item))}
122 | //通过给SortableList 设置最小拖动距离来激活点击事件(distance 单位px) 123 |
124 | 131 | {/* {apps.map((app, i) => renderCard(app, i))} */} 132 |
133 | ); 134 | } 135 | -------------------------------------------------------------------------------- /src/components/Apps/SetApp/SetApp.css: -------------------------------------------------------------------------------- 1 | .set_apps { 2 | display: flex; 3 | } 4 | .icon_list { 5 | height: 89%; 6 | overflow-y: scroll; 7 | display: grid; 8 | grid-template-columns: repeat(3, calc(96% / 3)); 9 | grid-column-gap: 2%; 10 | justify-items: center; 11 | margin-top: 3%; 12 | } 13 | .icon_list_item { 14 | width: 100%; 15 | height: 150px; 16 | overflow: hidden; 17 | position: relative; 18 | margin-bottom: 5%; 19 | background-color: #ffffff66; 20 | /* background-image: linear-gradient( 135deg, #3C8CE7 10%, #00EAFF 100%); */ 21 | border-radius: 25px; 22 | } 23 | 24 | .icon_list_item span { 25 | margin-left: 5%; 26 | color: white; 27 | font-size: 15px; 28 | /* white-space: nowrap; */ 29 | display: inline-block; 30 | width: 60%; 31 | position: relative; 32 | top: 5%; 33 | overflow-x: hidden; 34 | white-space: nowrap; 35 | text-overflow: ellipsis; 36 | } 37 | .icon_list_item div { 38 | margin: 5% 5% 5% 5%; 39 | overflow-x: hidden; 40 | white-space: nowrap; 41 | text-overflow: ellipsis; 42 | } 43 | .icon_list_item div.cover { 44 | position: absolute; 45 | width: 100%; 46 | height: 100%; 47 | margin: 0; 48 | transition: 0.3s ease-in-out; 49 | } 50 | .icon_list_item div.cover:hover { 51 | background-color: #00000066; 52 | } 53 | .icon_list_item button { 54 | position: absolute; 55 | top: 50%; 56 | left: 50%; 57 | transform: translate(-50%, -50%); 58 | opacity: 0; 59 | } 60 | .icon_list_item div.cover:hover button { 61 | opacity: 1; 62 | background-color: #fff3; 63 | } 64 | -------------------------------------------------------------------------------- /src/components/Calendar/CalComponent.css: -------------------------------------------------------------------------------- 1 | /* .cal{ 2 | 3 | width: 352px; 4 | height: 165px; 5 | 6 | padding: 16px; 7 | background: #FFFFFF; 8 | box-sizing: border-box; 9 | border-radius: 20px; 10 | display: inline-flex; 11 | justify-content: space-between; 12 | transition: .1s ease-in; 13 | } */ 14 | .cal-left { 15 | width: 40%; 16 | flex-direction: column; 17 | position: absolute; 18 | top: 55px; 19 | left: 0; 20 | } 21 | .cal-right { 22 | height: 100%; 23 | width: 60%; 24 | display: flex; 25 | position: absolute; 26 | right: 13px; 27 | top: 0; 28 | align-items: center; 29 | justify-content: center; 30 | } 31 | 32 | .cal-header { 33 | display: flex; 34 | flex-direction: row; 35 | align-items: center; 36 | justify-content: flex-start; 37 | } 38 | 39 | .cal-title { 40 | font-style: normal; 41 | font-weight: bold; 42 | font-size: 18px; 43 | line-height: 22px; 44 | letter-spacing: 0.2rem; 45 | margin-left: 0.6em; 46 | color: rgba(0, 0, 0, 0.85); 47 | cursor: pointer; 48 | } 49 | 50 | .cal-icon { 51 | width: 6px; 52 | height: 14px; 53 | left: 18px; 54 | top: 17px; 55 | background: linear-gradient(180deg, #ff6d90 14.58%, #ff3131 100%); 56 | box-shadow: 0px 3px 6px rgba(255, 55, 55, 0.8); 57 | border-radius: 20px; 58 | } 59 | 60 | .cal-date-container { 61 | width: 100%; 62 | height: calc(100% - 18px); 63 | display: flex; 64 | flex-direction: column; 65 | align-items: center; 66 | justify-content: center; 67 | } 68 | 69 | .cal-todo-container { 70 | width: 80%; 71 | display: flex; 72 | flex-direction: row; 73 | align-items: center; 74 | justify-content: center; 75 | margin: 0 auto; 76 | text-overflow: ellipsis; 77 | overflow: hidden; 78 | } 79 | 80 | .cal-weekday { 81 | font-size: 1rem; 82 | color: #ff4848; 83 | } 84 | 85 | .cal-date { 86 | font-size: 2.4rem; 87 | font-weight: lighter; 88 | color: #1d1d1d; 89 | } 90 | 91 | .cal-icon2 { 92 | width: 10px; 93 | height: 10px; 94 | border-radius: 50%; 95 | margin-right: 5px; 96 | /* background: #64C2F7; */ 97 | background-color: rgb(243, 104, 104); 98 | } 99 | 100 | .cal-todo-exist { 101 | color: rgba(0, 0, 0, 0.8); 102 | } 103 | 104 | .cal-todo-none { 105 | font-size: 0.9em; 106 | color: rgba(0, 0, 0, 0.5); 107 | } 108 | 109 | .react-calendar { 110 | width: 280px; 111 | background-color: #fff0; 112 | color: #222; 113 | border-radius: 8px; 114 | font-family: Arial, Helvetica, sans-serif; 115 | line-height: 1.125em; 116 | } 117 | .react-calendar__navigation { 118 | display: flex; 119 | justify-content: center; 120 | align-items: center; 121 | } 122 | .react-calendar__navigation__label { 123 | width: 80%; 124 | } 125 | .react-calendar__navigation button { 126 | min-width: 16px; 127 | background: none; 128 | font-size: 14px; 129 | margin-top: 8px; 130 | border: #fff; 131 | } 132 | .react-calendar__month-view__weekdays__weekday { 133 | color: rgba(0, 0, 0, 0.5); 134 | display: flex; 135 | justify-content: center; 136 | align-items: center; 137 | font-size: 12px; 138 | } 139 | .react-calendar__month-view__days__day { 140 | font-size: 12px; 141 | border: #fff; 142 | background-color: #fff; 143 | } 144 | .react-calendar__navigation button:enabled:hover, 145 | .react-calendar__navigation button:enabled:focus { 146 | background-color: #f8f8fa; 147 | } 148 | .react-calendar__navigation button[disabled] { 149 | background-color: #f0f0f0; 150 | } 151 | abbr[title] { 152 | text-decoration: none; 153 | } 154 | .react-calendar__tile { 155 | border-radius: 2px; 156 | border: 0; 157 | background-color: #fff0; 158 | } 159 | .react-calendar__tile:enabled:hover, 160 | .react-calendar__tile:enabled:focus { 161 | background: #f8f8fa; 162 | color: #6f48eb; 163 | border-radius: 6px; 164 | } 165 | .react-calendar__tile--now { 166 | background: #6f48eb33; 167 | border-radius: 6px; 168 | font-weight: bold; 169 | color: #6f48eb; 170 | } 171 | .react-calendar__tile--now:enabled:hover, 172 | .react-calendar__tile--now:enabled:focus { 173 | background: #6f48eb33; 174 | border-radius: 6px; 175 | font-weight: bold; 176 | color: #6f48eb; 177 | } 178 | .react-calendar__tile--hasActive:enabled:hover, 179 | .react-calendar__tile--hasActive:enabled:focus { 180 | background: #f8f8fa; 181 | } 182 | .react-calendar__tile--active { 183 | background: #6f48eb; 184 | border-radius: 6px; 185 | font-weight: bold; 186 | color: white; 187 | } 188 | .react-calendar__tile--active:enabled:hover, 189 | .react-calendar__tile--active:enabled:focus { 190 | background: #6f48eb; 191 | color: white; 192 | } 193 | .react-calendar--selectRange .react-calendar__tile--hover { 194 | background-color: #f8f8fa; 195 | } 196 | .react-calendar__tile--range { 197 | background: #f8f8fa; 198 | color: #6f48eb; 199 | border-radius: 0; 200 | } 201 | .react-calendar__tile--rangeStart { 202 | border-radius: 6px; 203 | background: #6f48eb; 204 | color: white; 205 | } 206 | .react-calendar__tile--rangeEnd { 207 | border-radius: 6px; 208 | background: #6f48eb; 209 | color: white; 210 | } 211 | 212 | div.todo { 213 | width: 90%; 214 | height: 2px; 215 | margin-top: -2px; 216 | border-radius: 1rem; 217 | background-color: #ff4848; 218 | } 219 | .cal-item { 220 | height: 20px; 221 | display: flex; 222 | justify-content: center; 223 | flex-direction: column; 224 | align-items: center; 225 | } 226 | .cal-detail { 227 | width: 60%; 228 | /* margin-bottom: 2%; */ 229 | margin-right: 1%; 230 | padding: 5px; 231 | background-color: #f0f0f088; 232 | font-size: 25px; 233 | /* border: 1px solid grey; */ 234 | } 235 | .cal-detail-item { 236 | height: 60px; 237 | font-weight: 700; 238 | font-size: 25px; 239 | display: flex; 240 | flex-direction: column; 241 | align-items: center; 242 | justify-content: center; 243 | position: relative; 244 | } 245 | .is-work { 246 | position: absolute; 247 | width: 20px; 248 | height: 20px; 249 | font-size: 12px; 250 | text-align: center; 251 | line-height: 20px; 252 | font-weight: 700; 253 | color: #fff; 254 | background: #4ed5f7; 255 | right: 0; 256 | top: 3px; 257 | } 258 | .work { 259 | background: #f74e4e; 260 | } 261 | .date-order { 262 | font-size: 20px; 263 | font-weight: 700; 264 | text-align: center; 265 | margin: 2% 0; 266 | } 267 | .date-order span { 268 | color: #6f48eb; 269 | font-size: 25px; 270 | margin: 0 5px; 271 | } 272 | .date-info { 273 | margin: 2% 0 2% 8%; 274 | width: 98%; 275 | font-size: 18px; 276 | } 277 | .date-info > span { 278 | padding: 2px 4px; 279 | color: #222; 280 | text-align: center; 281 | font-weight: 800; 282 | letter-spacing: 2px; 283 | margin-right: 1%; 284 | } 285 | .date-info > span:nth-child(1) { 286 | border: 1px solid red; 287 | background: #f74e4e55; 288 | font-size: 15px; 289 | margin-right: 3%; 290 | border-radius: 8px; 291 | } 292 | -------------------------------------------------------------------------------- /src/components/ChatRoom/ChatRoom.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/src/components/ChatRoom/ChatRoom.css -------------------------------------------------------------------------------- /src/components/ChatRoom/ChatRoom.js: -------------------------------------------------------------------------------- 1 | import "./ChatRoom.css"; 2 | import FuncModal from "../FuncModal/FuncModal"; 3 | import { useState } from "react"; 4 | import { Button } from "antd"; 5 | import { memo } from "react"; 6 | 7 | function ChatRoom() { 8 | // modal组件控制函数 9 | const [isModalVisible, setIsModalVisible] = useState(false); 10 | const showModal = () => { 11 | // openNotification(); 12 | setIsModalVisible(true); 13 | }; 14 | const handleOk = () => { 15 | setIsModalVisible(false); 16 | }; 17 | const handleCancel = () => { 18 | setIsModalVisible(false); 19 | }; 20 | 21 | return ( 22 | <> 23 | 26 | 33 |
46 | 建造中 ... 47 |
48 |
49 | 50 | ); 51 | } 52 | 53 | export default memo(ChatRoom); 54 | -------------------------------------------------------------------------------- /src/components/ClickMenu/ClickMenu.css: -------------------------------------------------------------------------------- 1 | .menu-wrapper { 2 | width: 120px; 3 | background: #fffd; 4 | border-radius: 10px; 5 | position: absolute; 6 | z-index: 4; 7 | } 8 | .menu-wrapper ul { 9 | padding-inline-start: 0; 10 | margin-bottom: 0; 11 | font-family: "SimHei"; 12 | } 13 | .menu-wrapper i { 14 | font-style: normal; 15 | font-size: 1rem; 16 | font-weight: 600; 17 | letter-spacing: 1px; 18 | user-select: none; 19 | } 20 | .menu-wrapper .menu { 21 | padding: 3px 5px; 22 | } 23 | .menu-content .menu-item { 24 | list-style: none; 25 | /* font-size: 10px; */ 26 | height: 30px; 27 | display: flex; 28 | /* padding:0 5px; */ 29 | align-items: center; 30 | border-radius: 5px; 31 | margin-bottom: 2px; 32 | /* background: #0003; */ 33 | } 34 | .menu-item > div { 35 | height: 100%; 36 | width: 100%; 37 | display: flex; 38 | align-items: center; 39 | cursor: pointer; 40 | } 41 | .menu-content .menu-item:hover { 42 | background: #0002; 43 | } 44 | .menu-content .menu-item span { 45 | /* font-size: 10px; */ 46 | margin-left: 8px; 47 | } 48 | -------------------------------------------------------------------------------- /src/components/ClickMenu/ClickMenu.js: -------------------------------------------------------------------------------- 1 | import "./ClickMenu.css"; 2 | import { 3 | DownloadOutlined, 4 | SwapOutlined, 5 | CodeOutlined, 6 | } from "@ant-design/icons"; 7 | import { memo, useState } from "react"; 8 | import { useSelector, useDispatch } from "react-redux"; 9 | import { SetFunctionArea } from "../FunctionAera/SetFunctionAera/SetFunctionArea"; 10 | import SetApp from "../Apps/SetApp/SetApp"; 11 | import { SnippetsInMenu } from "../Snippets/Snippets"; 12 | 13 | function ClickMenu() { 14 | const currentbg = useSelector(state => state.currentbg); 15 | const bgType = useSelector(state => state.bgtype); 16 | const clear = useSelector(state => state.clear); 17 | 18 | const dispatch = useDispatch(); 19 | 20 | const downloadWallPaper = e => { 21 | // e.stopPropagation(); 22 | //创造 a 标签来下载 23 | // 方法一:利用a标签下载,但是当跨域时,会跳转而不是下载 24 | const a = document.createElement("a"); 25 | a.style.display = "none"; 26 | a.href = currentbg; 27 | a.target = "_blank"; 28 | a.download = "bg.jpg"; 29 | document.body.appendChild(a); 30 | a.click(); // 自动触发点击a标签的click事件 31 | document.body.removeChild(a); 32 | // 方法二:获取文件二进制数据然后创建a标签下载 33 | // fetch(currentbg).then(res => { 34 | // res.blob().then(blob => { 35 | // const blobUrl = window.URL.createObjectURL(blob); 36 | // // 这里的文件名根据实际情况从响应头或者url里获取 37 | // // const filename = 'user.txt'; 38 | // const a = document.createElement("a"); 39 | // a.href = blobUrl; 40 | // a.download = "a.jpg"; 41 | // a.click(); 42 | // window.URL.revokeObjectURL(blobUrl); 43 | // }); 44 | // }); 45 | // 方法三:利用canvas编辑base64下载 46 | // let imgsrc = currentbg; 47 | // let image = new Image(); 48 | // image.crossOrigin = "anonymous"; 49 | // image.src = imgsrc; 50 | // // 解决跨域canvas污染问题 51 | // image.onload = () => { 52 | // let canvas = document.createElement("canvas"); 53 | // canvas.width = image.width; 54 | // canvas.height = image.height; 55 | // let context = canvas.getContext("2d"); 56 | // context.drawImage(image, 0, 0, image.width, image.height); 57 | // let url = canvas.toDataURL("image/png"); // 得到图片的base64编码数据 58 | // let a = document.createElement("a"); 59 | // a.download = "download"; 60 | // a.href = url; 61 | // a.click(); 62 | // }; 63 | }; 64 | 65 | function onChangeClear() { 66 | let value = clear ? 0 : 1; 67 | dispatch({ 68 | type: "CHANGE_CLEAR", 69 | clear: value, 70 | }); 71 | } 72 | 73 | return ( 74 |
75 |
76 | 103 |
104 |
105 | ); 106 | } 107 | 108 | export default memo(ClickMenu); 109 | -------------------------------------------------------------------------------- /src/components/Clock/Clock.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "UnidreamLED"; 3 | src: url(../../font/UnidreamLED.eot); /***兼容ie9***/ 4 | src: url(../../font/UnidreamLED.eot?#iefix) format("embedded-opentype"), 5 | /***兼容ie6-ie8***/ url("../../font/UnidreamLED.woff") format("woff"), 6 | local("UnidreamLED"), url("../../font/UnidreamLED.woff"); /***默认使用本地的***/ 7 | } 8 | @font-face { 9 | font-family: "DS-Digital"; 10 | /* 这个是定义字体的名称 */ 11 | src: url("../../font/DS-Digital\ Bold.ttf"); 12 | } 13 | 14 | .clock { 15 | position: absolute; 16 | left: 50%; 17 | transform: translateX(-50%); 18 | display: flex; 19 | flex-direction: column; 20 | justify-content: center; 21 | /* height: 20vh; */ 22 | padding: 0 3%; 23 | text-align: center; 24 | border-radius: 20px; 25 | /* align-items: center; */ 26 | font-size: 6.5rem; 27 | font-weight: 200; 28 | color: white; 29 | /* display: block; */ 30 | cursor: pointer; 31 | user-select: none; 32 | transition: 0.3s ease-in-out; 33 | height: 13rem; 34 | /* padding: 1%; */ 35 | } 36 | .clock:hover { 37 | background-color: #00000055; 38 | border-radius: 20px; 39 | } 40 | .clock-setting { 41 | font-size: 1.5rem; 42 | visibility: hidden; 43 | position: absolute; 44 | font-weight: 700; 45 | color: aqua; 46 | top: 5%; 47 | right: 3%; 48 | } 49 | .clock-day { 50 | /* position:absolute; */ 51 | font-size: 1.5rem; 52 | } 53 | .clock:hover .clock-setting { 54 | visibility: visible; 55 | } 56 | .digitalfont { 57 | font-family: "DS-Digital"; 58 | font-size: 8.5rem; 59 | } 60 | .h, 61 | .m { 62 | display: inline-block; 63 | /* line-height: 12rem; */ 64 | /* font-family: 'DS-Digital'; */ 65 | /* font-size: 100px; */ 66 | width: 9rem; 67 | } 68 | .h { 69 | text-align: right; 70 | margin-right: 2rem; 71 | } 72 | .m { 73 | text-align: left; 74 | margin-left: 2rem; 75 | } 76 | 77 | /* 倒计时 */ 78 | .countdown_content_clock { 79 | /* overflow-y: scroll; */ 80 | height: 10rem; 81 | /* margin-top: 2rem; */ 82 | /* position: relative; 83 | top:1rem; */ 84 | /* border:1px solid red; */ 85 | display: flex; 86 | flex-direction: column; 87 | justify-content: space-around; 88 | /* margin-top:10px; */ 89 | /* scroll-snap-type: y mandatory; */ 90 | /* scrollbar-width:none; */ 91 | } 92 | .countdown_content_clock > div { 93 | /* height:120px; */ 94 | /* margin-top: 30px; */ 95 | scroll-snap-align: end; 96 | } 97 | 98 | .countdown_content_clock > div div:nth-child(1) { 99 | text-align: center; 100 | font-size: 2rem; 101 | font-weight: 800; 102 | letter-spacing: 5px; 103 | color: #19bbfc; 104 | } 105 | .countdown_content_clock > div div:nth-child(2) { 106 | text-align: center; 107 | /* margin-top: 5px; */ 108 | font-size: 8rem; 109 | font-weight: 900; 110 | letter-spacing: 8px; 111 | } 112 | .countdown_content_clock span { 113 | font-size: 2rem; 114 | letter-spacing: 0px; 115 | } 116 | /* .countdown_inclock_item{ 117 | position: relative; 118 | top:0rem; 119 | } 120 | .countdown_slidein{ 121 | animation: countdown_slidin 1s; 122 | } 123 | @keyframes countdown_slidein { 124 | 0% { 125 | opacity: 0; 126 | top:3rem; 127 | } 128 | 100% { 129 | opacity: 1; 130 | top:0rem; 131 | } 132 | } */ 133 | 134 | /* 滚动条样式 */ 135 | .countdown_content_clock::-webkit-scrollbar { 136 | width: 0px; 137 | } 138 | /* .top-clock > div { 139 | position: absolute; 140 | left: -85%; 141 | z-index: 4; 142 | display: none; 143 | background-color: #0009; 144 | margin-top: 0.5vh; 145 | border-radius: 16px; 146 | padding: 5px 16px; 147 | width: 14vw; 148 | } 149 | .top-clock:hover > div { 150 | display: flex; 151 | justify-content: center; 152 | flex-direction: column; 153 | } 154 | .top-clock > div > div { 155 | display: flex; 156 | justify-content: space-between; 157 | } */ 158 | 159 | .top-clock > div { 160 | position: absolute; 161 | left: -85%; 162 | z-index: 4; 163 | display: none; 164 | background-color: #0009; 165 | margin-top: 0.5vh; 166 | border-radius: 16px; 167 | padding: 5px 16px; 168 | width: 14vw; 169 | } 170 | .top-clock:hover > div { 171 | display: flex; 172 | justify-content: center; 173 | flex-direction: column; 174 | } 175 | .city-time { 176 | display: flex; 177 | justify-content: space-between; 178 | } 179 | -------------------------------------------------------------------------------- /src/components/Clock/SetClock/SetClock.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/src/components/Clock/SetClock/SetClock.css -------------------------------------------------------------------------------- /src/components/Clock/SetClock/SetClock.js: -------------------------------------------------------------------------------- 1 | import "./SetClock.css"; 2 | import { Radio } from "antd"; 3 | import { React } from "react"; 4 | import { useDispatch, useSelector } from "react-redux"; 5 | import useLocalStorage from "../../../hooks/useLocalStorage"; 6 | 7 | export default function SetClock() { 8 | const dispatch = useDispatch(); 9 | // const [value, setValue] = useState(1); 10 | const [, setTimeFont] = useLocalStorage("timefont"); 11 | const TimeFont = useSelector(state => state.timefont); 12 | 13 | const onChange = e => { 14 | // setValue(e.target.value); 15 | dispatch({ 16 | type: "CHANGE_TIMEFONT", 17 | timefont: e.target.value, 18 | }); 19 | setTimeFont(e.target.value); 20 | }; 21 | 22 | return ( 23 |
24 |
25 | 时间样式 26 | 27 | 28 | 普通 29 | 数码 30 | 31 | 32 |
33 |
34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/components/Competition/Competition.css: -------------------------------------------------------------------------------- 1 | .competition { 2 | /* width: 18vw; 3 | height: 8.5vw; */ 4 | width: 352px; 5 | height: 165px; 6 | /* margin:20px; */ 7 | /* left:764px; 8 | top:20px; */ 9 | display: inline-block; 10 | background: #ffffff; 11 | box-sizing: border-box; 12 | border-radius: 20px; 13 | transition: 0.1s ease-in; 14 | } 15 | 16 | /* .competition .com_left { 17 | display: inline-block; 18 | margin-top: 13px; 19 | } 20 | .competition .com_left p{ 21 | 22 | font-style: normal; 23 | font-weight: bold; 24 | font-size: 18px; 25 | line-height: 22px; 26 | margin: 0; 27 | display: inline-block; 28 | letter-spacing: 0.2rem; 29 | cursor: pointer; 30 | margin-left: 12px; 31 | } 32 | .competition .com_left div{ 33 | display: inline-block; 34 | margin-left: 18px; 35 | width: 6px; 36 | height: 14px; 37 | background: linear-gradient(180deg, #6d53b4 14.58%, #2a1086 100%); 38 | box-shadow: 0px 3px 6px rgba(7, 87, 119, 0.8); 39 | border-radius: 20px; 40 | } 41 | .competition .com_right{ 42 | display: inline-flex; 43 | width: 150px; 44 | height: 25px; 45 | position: absolute; 46 | margin-top: 12px; 47 | margin-left: 65px; 48 | justify-content: space-around; 49 | } 50 | .com_right span{ 51 | font-size: 15px; 52 | line-height: 25px; 53 | text-align: center; 54 | font-weight: 700; 55 | width: 50px; 56 | height: 25px; 57 | border-radius: 5px; 58 | cursor: pointer; 59 | } */ 60 | /* .com_right span:hover{ 61 | background-color:#00000022; 62 | } */ 63 | .com_board { 64 | width: 95%; 65 | height: 120px; 66 | /* background-color:#00000011; */ 67 | margin: 5px auto 0 auto; 68 | /* overflow-y: scroll; */ 69 | border-radius: 20px; 70 | } 71 | .com_board::-webkit-scrollbar { 72 | width: 0px; 73 | } 74 | .com_board a { 75 | /* display: block; */ 76 | color: #000000; 77 | } 78 | .com_board a > div { 79 | /* overflow-y: scroll; */ 80 | height: 120px; 81 | /* scroll-snap-type: y mandatory; */ 82 | /* 滚动贴合 */ 83 | } 84 | .com_board a > div::-webkit-scrollbar { 85 | width: 0; 86 | } 87 | .nba { 88 | display: flex; 89 | width: 100%; 90 | height: 100%; 91 | margin-top: -1.5%; 92 | justify-content: space-around; 93 | /* scroll-snap-align: start; */ 94 | } 95 | .nba_team { 96 | display: inline-flex; 97 | flex-direction: column; 98 | align-items: center; 99 | font-size: 20px; 100 | font-weight: 700; 101 | height: 120px; 102 | margin-top: 10px; 103 | overflow: hidden; 104 | word-wrap: normal; 105 | text-overflow: ellipsis; 106 | } 107 | 108 | .nba_team img { 109 | height: 80px; 110 | } 111 | 112 | .score_time { 113 | font-size: 30px; 114 | font-weight: 800; 115 | font-family: "SimHei, Serif"; 116 | margin-top: 30px; 117 | text-align: center; 118 | } 119 | .score_time div { 120 | font-size: 15px; 121 | font-weight: 400; 122 | } 123 | -------------------------------------------------------------------------------- /src/components/Competition/Competition.js: -------------------------------------------------------------------------------- 1 | import "./Competition.css"; 2 | import { memo, useEffect, useState } from "react"; 3 | import FuncCard from "../FuncCard/FuncCard"; 4 | 5 | //无比赛页面 6 | const no_game = ( 7 |
19 | 今日无赛程 20 |
21 | ); 22 | 23 | function NBA(props) { 24 | //修复了一个bug,但是还没弄清原因,初步判断是state的更新问题 25 | //还差一个滚动贴合 26 | const [games, setGames] = useState([]); 27 | const [itemIndex, setItemIndex] = useState(0); 28 | 29 | //直接来自远程NBA网站的图片 30 | const remote_logo_img = "https://res.nba.cn/media/img/teams/logos/"; 31 | 32 | let flag = []; //用来判断是否有比赛在进行中 33 | 34 | const handleWheelCapture = e => { 35 | if (games.length > 1) { 36 | e.stopPropagation(); 37 | } 38 | if (e.deltaY > 0 && itemIndex < games.length - 1) { 39 | setItemIndex(itemIndex + 1); 40 | } 41 | if (e.deltaY < 0 && itemIndex >= 1) { 42 | setItemIndex(itemIndex - 1); 43 | } 44 | }; 45 | 46 | useEffect(() => { 47 | let url = "https://api.nba.cn/sib/v2/game/schedule"; 48 | async function getGameList() { 49 | fetch(url) 50 | .then(response => response.json()) 51 | .then( 52 | data => { 53 | setGames(data.data.groups[0].games); 54 | flag = data.data.groups[0].games.map((item, index) => item.status); 55 | } //.next.games 表示下一天 56 | ) 57 | .catch(e => console.log("error")); 58 | } 59 | getGameList(); 60 | 61 | const t = setInterval(() => { 62 | // let flag = games.map((item,index)=>item.boxscore.status) //获取比赛状态数组,如果有比赛正在进行才请求 这个地方获取不行 63 | // console.log(games) 64 | if (flag.includes(2)) { 65 | console.log("比赛进行中"); 66 | getGameList(); 67 | } 68 | }, 10000); //10s更新一次 69 | 70 | return () => { 71 | clearTimeout(t); 72 | }; 73 | }, []); 74 | 75 | const have_game = games.map((item, index) => { 76 | let awayTeam = item.awayTeamName; 77 | let homeTeam = item.homeTeamName; 78 | // let time = new Date(item.dateTimeUtc); 79 | // time = new Date(time.setHours(time.getHours() + 13)); 80 | // time = time.getHours() + " : " + time.toLocaleTimeString().slice(3, 5); 81 | let time = item.startTime.slice(0, 2) + " : " + item.startTime.slice(3, 5); 82 | let score = item.awayTeamScore + " - " + item.homeTeamScore; 83 | let score_time = item.status !== 1 ? score : time; 84 | // 根据不同比赛状态显示不同文字 85 | let status = 86 | item.status == 2 87 | ? item.periodText + " " + item.gameClock 88 | : item.statusText; 89 | // 详细信息跳转链接 90 | let link = 91 | item.status == 1 92 | ? "https://china.nba.cn/preview/" + item.gameId + "/game-list" 93 | : "https://china.nba.cn/game/" + item.gameId + "/box-score"; 94 | if (index === itemIndex) 95 | return ( 96 |
97 | 98 | logo 102 | {awayTeam} 103 | 104 | 105 | 106 | {score_time} 107 |
116 | {status} 117 |
118 |
119 |
120 | 121 | logo 125 | {homeTeam} 126 | 127 |
128 | ); 129 | }); 130 | 131 | // const no_game =
今日无赛程
132 | 133 | return ( 134 |
135 | {games.length === 0 ? no_game : have_game} 136 |
137 | ); 138 | } 139 | 140 | const Competition = () => { 141 | const [type, setType] = useState(0); //比赛类型 142 | const [game, setGame] = useState([]); 143 | 144 | const handleChangeType = value => { 145 | setType(value); 146 | }; 147 | 148 | return ( 149 | <> 150 | 160 | {/*

比赛信息

*/} 161 | {/*
162 | setType(0)} style={{backgroundColor:(type===0? '#00000022':'#ffffff')}}>NBA 163 | setType(1)} style={{backgroundColor:(type===1? '#00000022':'#ffffff')}}>LOL 164 | setType(2)} style={{backgroundColor:(type===2? '#00000022':'#ffffff')}}>围棋 165 |
*/} 166 | {/* 167 | NBA官网: https://china.nba.cn/ 168 | 腾讯视频: https://v.qq.com/channel/nba 169 | */} 170 |
171 | {/* 这里是FuncCard 尝试封装type的一个尝试 */} 172 | 178 | 179 | 180 | 186 | {no_game} 187 | 188 | 194 | {no_game} 195 | 196 | {/*
197 |
*/} 198 |
199 |
200 | 201 | ); 202 | }; 203 | 204 | export default memo(Competition); 205 | -------------------------------------------------------------------------------- /src/components/CountDown/CountDown.css: -------------------------------------------------------------------------------- 1 | .CountDown { 2 | /* width: 18vw; 3 | height: 8.5vw; */ 4 | width: 352px; 5 | height: 165px; 6 | background: #ffffff; 7 | box-sizing: border-box; 8 | border-radius: 20px; 9 | transition: 0.1s ease-in; 10 | } 11 | .countdown_content { 12 | overflow-y: scroll; 13 | height: 120px; 14 | /* margin-top:10px; */ 15 | scroll-snap-type: y mandatory; 16 | /* scrollbar-width:none; */ 17 | } 18 | .countdown_content > div { 19 | /* height:120px; */ 20 | margin-top: 30px; 21 | scroll-snap-align: end; 22 | } 23 | 24 | .countdown_content > div div:nth-child(1) { 25 | text-align: center; 26 | font-size: 20px; 27 | font-weight: 800; 28 | letter-spacing: 3px; 29 | color: #31c5ff; 30 | } 31 | .countdown_content > div div:nth-child(2) { 32 | text-align: center; 33 | margin-top: 5px; 34 | font-size: 55px; 35 | font-family: "Microsoft Yahei"; 36 | font-weight: 800; 37 | letter-spacing: 4px; 38 | } 39 | .countdown_content span { 40 | font-size: 30px; 41 | margin-left: 2%; 42 | letter-spacing: 0; 43 | } 44 | 45 | /* 滚动条样式 */ 46 | .countdown_content::-webkit-scrollbar { 47 | width: 0px; 48 | } 49 | /* 设置滚动滑块 */ 50 | /* .newsList::-webkit-scrollbar-thumb { 51 | border-radius: 10px; 52 | box-shadow: inset 0 0 4px #00000033; 53 | background: rgba(0,0,0,0.2); 54 | } 55 | .newsList::-webkit-scrollbar-track { 56 | box-shadow: inset 0 0 5px rgba(0,0,0,0.2); 57 | border-radius: 10px; 58 | background: rgba(0,0,0,0.1); 59 | } */ 60 | 61 | /* CSS实现滚动贴合 用在组件里面*/ 62 | /* main{ 63 | scroll-snap-type:y mandatory; 强制滚动 64 | scroll-padding: 80px; 贴合时的padding,如有粘滞元素的时候 65 | } 66 | section{ 67 | scroll-snap-align: start; 贴合的位置,底部,center,或者顶部 68 | } */ 69 | -------------------------------------------------------------------------------- /src/components/Demos/Demos.css: -------------------------------------------------------------------------------- 1 | .demo { 2 | width: 300px; 3 | height: 150px; 4 | position: absolute; 5 | border-radius: 10px; 6 | background: #fff0; 7 | z-index: 1; 8 | transition: 0.3s ease-in-out; 9 | backdrop-filter: blur(10px) brightness(0.5); 10 | /* border: 1px solid blue; */ 11 | } 12 | .demo:hover { 13 | /* background:#0008; */ 14 | backdrop-filter: blur(0); 15 | } 16 | .demo > div { 17 | position: relative; 18 | left: 50%; 19 | top: 50%; 20 | transform: translate(-50%, -50%); 21 | opacity: 1; 22 | /* background-color: #fff0; */ 23 | font-size: 40px; 24 | font-weight: 700; 25 | color: #fffb; 26 | text-align: center; 27 | letter-spacing: 2px; 28 | transition: 0.3s ease-in; 29 | } 30 | .demo:hover > div { 31 | opacity: 0; 32 | /* background-color: #000a; */ 33 | } 34 | -------------------------------------------------------------------------------- /src/components/Demos/Demos.js: -------------------------------------------------------------------------------- 1 | import "./Demos.css"; 2 | import { memo, useState } from "react"; 3 | import FuncCard from "../FuncCard/FuncCard"; 4 | import FuncModal from "../FuncModal/FuncModal"; 5 | import Lottie from "react-lottie"; 6 | import motorbike from "../../asset/motorbike.json"; 7 | import GitHubCalendar from "react-github-calendar"; 8 | // import astronaut from '../../asset/astronaut.json'; 9 | // import solarsystem from '../../asset/solarsystem.json' 10 | 11 | const Demos = () => { 12 | const demoList = [ 13 | { 14 | name: "parallax", 15 | imgPath: 16 | "https://minio.wisdompanda.com/newtabimg/demoimg/mydemo_parallax.png", 17 | src: "https://wisdompanda.com/demo/parallax/", 18 | }, 19 | { 20 | name: "弹幕墙", 21 | imgPath: 22 | "https://minio.wisdompanda.com/newtabimg/demoimg/bulletscreen.png", 23 | src: "https://wisdompanda.com/demo/bulletscreen/", 24 | }, 25 | { 26 | name: "parallax", 27 | imgPath: 28 | "https://minio.wisdompanda.com/newtabimg/demoimg/mydemo_parallax.png", 29 | src: "https://wisdompanda.com/demo/parallax/", 30 | }, 31 | { 32 | name: "parallax", 33 | imgPath: 34 | "https://minio.wisdompanda.com/newtabimg/demoimg/mydemo_parallax.png", 35 | src: "https://wisdompanda.com/demo/parallax/", 36 | }, 37 | ]; 38 | 39 | // modal组件控制函数 40 | const [isModalVisible, setIsModalVisible] = useState(false); 41 | const showModal = () => { 42 | // openNotification(); 43 | setIsModalVisible(true); 44 | }; 45 | const handleOk = () => { 46 | setIsModalVisible(false); 47 | }; 48 | const handleCancel = () => { 49 | setIsModalVisible(false); 50 | }; 51 | // const [persistedTodoList] = us 52 | 53 | //lottie动画设定 54 | const defaultOptions = { 55 | loop: true, 56 | autoplay: true, 57 | animationData: motorbike, 58 | rendererSettings: { 59 | preserveAspectRatio: "xMidYMid slice", 60 | }, 61 | }; 62 | 63 | return ( 64 | 65 |
77 |
78 |
79 |
80 |
90 | 91 |
92 | 108 | 前端练习 109 | 110 | } 111 | visible={isModalVisible} 112 | onOk={handleOk} 113 | onCancel={handleCancel} 114 | // width={"57vw"} 115 | > 116 |
129 | {demoList.map((item, index) => { 130 | return ( 131 | 142 |
143 |
{item.name}
144 |
145 | 153 |
154 | ); 155 | })} 156 |
157 |
173 | 174 |
175 |
176 |
177 | ); 178 | }; 179 | 180 | export default memo(Demos); 181 | -------------------------------------------------------------------------------- /src/components/FormHabit/FormHabit.js: -------------------------------------------------------------------------------- 1 | import { memo } from "react"; 2 | import FuncCard from "../FuncCard/FuncCard"; 3 | 4 | function FormHabit() { 5 | return ( 6 | 14 | ); 15 | } 16 | 17 | export default memo(FormHabit); 18 | -------------------------------------------------------------------------------- /src/components/FuncCard/FuncCard.css: -------------------------------------------------------------------------------- 1 | .funcCard { 2 | position: relative; /* 试运行 */ 3 | /* width: 352px; */ 4 | height: 165px; 5 | background: #ececec; 6 | border-radius: 20px; 7 | overflow: hidden; 8 | /* background-color: #FF5631; */ 9 | /* display: flex; 10 | justify-content: center; 11 | align-items: center; */ 12 | transition: 0.2s ease-in; 13 | /* backdrop-filter: blur(50px); */ 14 | /* border: 1px solid red; */ 15 | box-shadow: 0px 10px 15px -3px rgba(0, 0, 0, 0.1); 16 | cursor: pointer; 17 | } 18 | .filter { 19 | background: #ffffff55; 20 | backdrop-filter: blur(20px); 21 | /* backdrop-filter: blur(2px); */ 22 | /* color: #FFFFFF; */ 23 | } 24 | /* .dark,.dark span,.dark div{ 25 | background-color: #000000; 26 | color: #FFFFFF; 27 | } */ 28 | 29 | .no-title { 30 | display: flex; 31 | justify-content: center; 32 | align-items: center; 33 | } 34 | .cardTitle { 35 | display: inline-block; 36 | margin-top: 0.8rem; 37 | margin-left: 0.8rem; 38 | font-style: normal; 39 | font-weight: bold; 40 | font-size: 18px; 41 | /* width: 50%; */ 42 | letter-spacing: 0.2rem; 43 | } 44 | .cardTitle div { 45 | display: inline-block; 46 | width: 6px; 47 | height: 14px; 48 | background: linear-gradient(180deg, #ffc531 14.58%, #ff5631 100%); 49 | box-shadow: 0px 3px 6px rgba(255, 141, 6, 0.8); 50 | /* box-shadow: 0px 3px 6px #000; */ 51 | border-radius: 20px; 52 | margin-right: 15px; 53 | margin-left: 5px; 54 | } 55 | .cardKinds { 56 | display: inline-flex; 57 | width: 150px; 58 | height: 25px; 59 | position: absolute; 60 | right: 15px; 61 | margin-top: 12px; 62 | justify-content: space-around; 63 | } 64 | .cardKinds span { 65 | font-size: 15px; 66 | line-height: 25px; 67 | text-align: center; 68 | font-weight: 700; 69 | width: 50px; 70 | height: 25px; 71 | border-radius: 5px; 72 | cursor: pointer; 73 | } 74 | -------------------------------------------------------------------------------- /src/components/FuncCard/FuncCard.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { memo, useState } from "react"; 3 | import { useSelector } from "react-redux"; 4 | import "./FuncCard.css"; 5 | 6 | /** 7 | * 8 | * 组件可选参数: 9 | * title (Stirng) 10 | * iconStyle (CSS Style) 11 | * kinds ([]) 12 | * width (String 没有就默认 352px) 13 | * changeType() 14 | * className 15 | * 16 | */ 17 | 18 | //加入filter:blur 19 | const FuncCard = props => { 20 | const cardstyle = useSelector(state => state.cardstyle); 21 | const { title, iconStyle, kinds, width } = props; 22 | 23 | const [type, setType] = useState(0); 24 | 25 | const if_title = title ? "" : " no-title"; //判断是否有标题来确定内容的布局方式 26 | 27 | const classlist = props.className ? " " + props.className : ""; //原来的类名 28 | 29 | const cardstyles = ["", " filter"]; 30 | 31 | useEffect(() => { 32 | // console.log("Effect " + title); 33 | // return () => { 34 | // console.log("destroy" + title); 35 | // }; 36 | }, []); 37 | 38 | return ( 39 |
43 | {title ? ( //标题 44 |
45 |
46 | {title} 47 |
48 | ) : null} 49 | {kinds ? ( //右边种类标签 50 |
51 | {kinds.map((item, index) => { 52 | return ( 53 | { 56 | setType(index); 57 | props.changeType(index); 58 | }} 59 | style={{ 60 | backgroundColor: type === index ? "#00000022" : "#ffffff00", 61 | }} 62 | > 63 | {item} 64 | 65 | ); 66 | })} 67 |
68 | ) : null} 69 | {props.children} 70 |
71 | ); 72 | }; 73 | 74 | export default memo(FuncCard); 75 | -------------------------------------------------------------------------------- /src/components/FuncCard/SetFuncCard/SetFuncCard.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/src/components/FuncCard/SetFuncCard/SetFuncCard.css -------------------------------------------------------------------------------- /src/components/FuncCard/SetFuncCard/SetFuncCard.js: -------------------------------------------------------------------------------- 1 | import "./SetFuncCard.css"; 2 | import { Radio } from "antd"; 3 | import { useState } from "react"; 4 | import { useDispatch, useSelector } from "react-redux"; 5 | import { Button } from "antd"; 6 | import useLocalStorage from "../../../hooks/useLocalStorage"; 7 | import FuncModal from "../../FuncModal/FuncModal"; 8 | 9 | export function SetFuncCardStyle() { 10 | const [cardStyle, setCardStyle] = useLocalStorage("cardstyle"); 11 | const cardstyle = useSelector(state => state.cardstyle); 12 | const dispatch = useDispatch(); 13 | 14 | const onChange = e => { 15 | //console.log('radio checked', e.target.value); 16 | // setValue(e.target.value); 17 | dispatch({ 18 | type: "CHANGE_CARDSTYLE", 19 | cardstyle: e.target.value, 20 | }); 21 | setCardStyle(e.target.value); 22 | }; 23 | 24 | return ( 25 |
26 | 卡片样式 27 | 28 | 29 | 普通 30 | 磨砂 31 | 32 | 33 |
34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/components/FuncModal/FuncModal.css: -------------------------------------------------------------------------------- 1 | /* Modal磨砂 */ 2 | .react-draggable .ant-modal-content { 3 | background: #ffffff55; 4 | /* -webkit-backdrop-filter: blur(20px); */ 5 | backdrop-filter: blur(20px); 6 | /* transition: all 2s ease-in-out; */ 7 | } 8 | .react-draggable .ant-modal-header { 9 | background: #ffffff00; 10 | border-bottom: 0; 11 | /* -webkit-backdrop-filter: blur(20px); */ 12 | backdrop-filter: blur(20px); 13 | /* transition: all 2s ease-in-out; */ 14 | } 15 | /* News磨砂 */ 16 | /* 修改tabpane的背景颜色 */ 17 | .ant-tabs-card.ant-tabs-left > .ant-tabs-nav .ant-tabs-tab { 18 | background: #ffffff00; 19 | } 20 | .ant-tabs-card > .ant-tabs-nav .ant-tabs-tab { 21 | border: 0; 22 | } 23 | .ant-tabs-card.ant-tabs-left > .ant-tabs-nav .ant-tabs-tab-active { 24 | background: #ffffffaa; 25 | } 26 | .ant-tabs-dropdown-menu { 27 | background: #ffffffaa; 28 | } 29 | .ant-tabs-dropdown-menu::-webkit-scrollbar { 30 | width: 3px; 31 | } 32 | .ant-tabs-dropdown-menu::-webkit-scrollbar-thumb { 33 | border-radius: 10px; 34 | box-shadow: inset 0 0 4px #00000033; 35 | background: rgba(0, 0, 0, 0.2); 36 | } 37 | .ant-tabs-dropdown-menu::-webkit-scrollbar-track { 38 | box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); 39 | border-radius: 10px; 40 | background: rgba(0, 0, 0, 0.1); 41 | } 42 | /* Table磨砂 */ 43 | .ant-table-thead > tr > th { 44 | background: #fafafa99; 45 | } 46 | .ant-table { 47 | background: #fff0; 48 | } 49 | .ant-table-tbody > tr.ant-table-row:hover > td, 50 | .ant-table-tbody > tr > td.ant-table-cell-row-hover { 51 | background: #fff5; 52 | } 53 | .ant-btn-dashed { 54 | background: #fff6; 55 | } 56 | .ant-btn-dashed:hover, 57 | .ant-btn-dashed:focus { 58 | background: #fff9; 59 | } 60 | 61 | .expand { 62 | border-radius: 0.1rem; 63 | position: absolute; 64 | padding: 3px 8px; 65 | z-index: 4; 66 | right: 1%; 67 | top: 0%; 68 | } 69 | .expand:hover { 70 | background-color: #0008; 71 | } 72 | -------------------------------------------------------------------------------- /src/components/FuncModal/FuncModal.js: -------------------------------------------------------------------------------- 1 | import { Modal } from "antd"; 2 | import { ExpandOutlined, CloseOutlined } from "@ant-design/icons"; 3 | import "./FuncModal.css"; 4 | import { useState, useRef, memo } from "react"; 5 | import Draggable from "react-draggable"; 6 | 7 | const FuncModal = props => { 8 | const [disabled, setDisabled] = useState(false); 9 | const [full, setFull] = useState(false); 10 | const [visible, setVisible] = useState(props.visible); 11 | const [bounds, setBounds] = useState({ 12 | left: 0, 13 | top: 0, 14 | bottom: 0, 15 | right: 0, 16 | }); 17 | const draggleRef = useRef(null); 18 | 19 | // 拖拽组件 20 | const onStart = (_event, uiData) => { 21 | const { clientWidth, clientHeight } = window.document.documentElement; 22 | const targetRect = draggleRef.current?.getBoundingClientRect(); 23 | 24 | if (!targetRect) { 25 | return; 26 | } 27 | 28 | setBounds({ 29 | left: -targetRect.left + uiData.x, 30 | right: clientWidth - (targetRect.right - uiData.x), 31 | top: -targetRect.top + uiData.y, 32 | bottom: clientHeight - (targetRect.bottom - uiData.y), 33 | }); 34 | }; 35 | 36 | const handleContextMenu = e => { 37 | e.stopPropagation(); 38 | }; 39 | 40 | return ( 41 |
handleContextMenu(e)}> 42 | ( 74 | onStart(event, uiData)} 78 | > 79 |
{modal}
80 |
81 | )} 82 | > 83 |
setFull(!full)} 87 | > 88 | 89 |
90 | {props.children} 91 |
92 |
93 | ); 94 | }; 95 | 96 | export default memo(FuncModal); 97 | -------------------------------------------------------------------------------- /src/components/FunctionAera/FunctionAera.css: -------------------------------------------------------------------------------- 1 | .functionAera { 2 | position: relative; 3 | bottom: 0vh; 4 | margin: 0 auto 30px auto; 5 | width: 84vw; 6 | height: 20vw; 7 | border-radius: 20px; 8 | border: 0px solid black; 9 | /* overflow-y: scroll; */ 10 | transition: 0.3s ease-in-out; 11 | /* transform: scale(0.95); */ 12 | /* filter: blur(0px)!important */ 13 | /* background-color: #ffffff4D; */ 14 | } 15 | .sortable { 16 | /* position: relative; 17 | z-index: 1; */ 18 | display: flex; 19 | flex-wrap: wrap; 20 | justify-content: center; 21 | /* display: inline-grid; 22 | grid-template-columns: repeat(4, 352px); 23 | grid-template-rows: repeat(auto-fill, 180px); */ 24 | } 25 | .sortableItem { 26 | position: relative; 27 | z-index: 2; 28 | margin: 12px; 29 | border-radius: 20px; 30 | /* background-image: url(https://minio.wisdompanda.com/background/bg1.jpg); 31 | background-size: cover; 32 | background-repeat: no-repeat; */ 33 | /* transform: scale(0.8); */ 34 | /* display: inline-block; */ 35 | /* transition: .5s ease-in; */ 36 | } 37 | /* .sortableItem:active{ 38 | transform: scale(0.95); 39 | } */ 40 | .sortableItem > button { 41 | position: absolute; 42 | right: 0; 43 | z-index: 1; 44 | } 45 | .funcshake { 46 | animation: func_shake 0.2s infinite; 47 | } 48 | @keyframes func_shake { 49 | 0% { 50 | transform: rotate(1deg); 51 | } 52 | 25% { 53 | transform: rotate(0); 54 | } 55 | 50% { 56 | transform: rotate(-1deg); 57 | } 58 | 75% { 59 | transform: rotate(0deg); 60 | } 61 | 100% { 62 | transform: rotate(1deg); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/components/FunctionAera/FunctionAera.js: -------------------------------------------------------------------------------- 1 | import "./FunctionAera.css"; 2 | import "../../font/iconfont.css"; 3 | import News from "../News/News"; 4 | import Todo from "../Todo/Todo"; 5 | import Pictures from "../Pictures"; 6 | import Notes from "../Notes"; 7 | import Weather from "../Weather/Weather"; 8 | import CalComponent from "../Calendar/CalComponent"; 9 | import Competition from "../Competition/Competition"; 10 | import CountDown from "../CountDown/CountDown"; 11 | import ServerMonitor from "../ServerMonitor/ServerMonitor"; 12 | import ToolKit from "../ToolKit/ToolKit"; 13 | import Demos from "../Demos/Demos"; 14 | // import Memo from '../Memo/Memo' 15 | // import YearToday from '../FuncModule/YearToday/YearToday' 16 | import TomatoClock from "../TomatoClock/TomatoClock"; 17 | 18 | import { SortableContainer, SortableElement } from "react-sortable-hoc"; 19 | import { arrayMoveImmutable } from "array-move"; 20 | import { useSelector, useDispatch } from "react-redux"; 21 | import { memo, useEffect, useState } from "react"; 22 | import { Button } from "antd"; 23 | import { CloseOutlined } from "@ant-design/icons"; 24 | import { funcs } from "./SetFunctionAera/SetFunctionArea"; 25 | 26 | //TODO: 1.服务器性能监控模块,2. 前端工具箱模块 3. 股票信息模块 4. 图片上传图床模块 5. 博客文章显示(改写笔记) 6. 日历添加打卡功能 7. 那年今日 8.桌面宠物或者人偶 27 | 28 | // type 0 为装饰类 1 为功能类 29 | // const funcs = [ 30 | // { id: 0, node: , type: "1" }, 31 | // { id: 1, node: , type: "1" }, 32 | // { id: 2, node: , type: "0" }, 33 | // { id: 3, node: , type: "1" }, 34 | // { id: 4, node: , type: "1" }, 35 | // { id: 5, node: , type: "1" }, 36 | // { id: 6, node: , type: "1" }, 37 | // { id: 7, node: , type: "1" }, 38 | // { id: 8, node: , type: "1" }, 39 | // { id: 9, node: , type: "1" }, 40 | // { id: 10, node: , type: "0" }, 41 | // // {id:11, node:}, 42 | // // {id:11, node:}, 43 | // { id: 11, node: , type: "1" }, 44 | // ]; 45 | 46 | //测试上传 47 | const FunctionAera = () => { 48 | //中间的功能组件,放在里面 49 | 50 | const funcdeleteMode = useSelector(state => state.deleteFunc); 51 | const functionList = useSelector(state => state.functionList); 52 | // let timePos = useSelector(state => state.timePos); 53 | 54 | //先重新排列这些组件,统一组件大小 55 | // let functionList = localStorage.getItem('functionList')? localStorage.getItem('functionList'):"[0,1,2,3,4,5,6,7]" 56 | //const functionList = useSelector(state=>state.functionList) 57 | const [items, setItems] = useState(functionList); 58 | 59 | const dispatch = useDispatch(); 60 | 61 | const funcshake = funcdeleteMode ? " funcshake" : ""; 62 | 63 | useEffect(() => { 64 | setItems(functionList); 65 | }, [functionList]); 66 | 67 | const handleContextMenu = (e, b) => { 68 | e.stopPropagation(); 69 | e.preventDefault(); 70 | dispatch({ 71 | type: "CHANGE_DELETEFUNC", 72 | deleteFunc: !b, 73 | }); 74 | }; 75 | 76 | const SortableItem = SortableElement(({ value }) => ( 77 |
handleContextMenu(e, funcdeleteMode)} 81 | > 82 | {/* 删除模式按钮 */} 83 |
92 | )); 93 | 94 | const SortableList = SortableContainer(({ items }) => { 95 | return ( 96 |
97 | {items.map((value, index) => ( 98 | console.log(value)} 100 | key={index} 101 | index={index} 102 | value={value} 103 | /> 104 | ))} 105 |
106 | ); 107 | }); 108 | 109 | const deleteFunc = id => { 110 | let newList = items; 111 | let cover = items.reduce((pre, cur) => { 112 | return pre + funcs[cur].cover; 113 | }, 0); 114 | // console.log(cover); 115 | //删除数组中指定元素 116 | // if (cover > 16) newList = newList.slice(0, 8); 117 | // console.log(newList); 118 | newList.splice(newList.indexOf(id), 1); 119 | setItems(newList); 120 | localStorage.setItem("functionList", JSON.stringify(newList)); 121 | // let updatemyApps = myApps.filter((item) => item.id !== id); 122 | // localStorage.setItem('apps', JSON.stringify(updatemyApps)) 123 | // setItems(updatemyApps); 124 | dispatch({ 125 | type: "CHANGE_FUNCS", 126 | functionList: newList, 127 | }); 128 | // console.log("deleteFunc") 129 | }; 130 | 131 | const onSortEnd = ({ oldIndex, newIndex }) => { 132 | setItems(arrayMoveImmutable(items, oldIndex, newIndex)); 133 | localStorage.setItem( 134 | "functionList", 135 | JSON.stringify(arrayMoveImmutable(items, oldIndex, newIndex)) 136 | ); 137 | }; 138 | 139 | let height = "30vw"; 140 | 141 | return ( 142 |
143 | 149 |
150 | ); 151 | }; 152 | 153 | export default memo(FunctionAera); 154 | -------------------------------------------------------------------------------- /src/components/FunctionAera/SetFunctionAera/SetFunctionArea.css: -------------------------------------------------------------------------------- 1 | .addfunc { 2 | height: 165px; 3 | position: absolute; 4 | border-radius: 20px; 5 | background: #fff0; 6 | z-index: 1; 7 | transition: 0.3s ease-in-out; 8 | } 9 | .addfunc:hover { 10 | background: #0005; 11 | } 12 | .addfunc > button { 13 | position: relative; 14 | left: 50%; 15 | top: 50%; 16 | transform: translate(-50%, -50%); 17 | opacity: 0; 18 | background-color: #fff0; 19 | } 20 | .addfunc:hover > button { 21 | opacity: 1; 22 | background-color: #fff8; 23 | } 24 | -------------------------------------------------------------------------------- /src/components/FunctionAera/SetFunctionAera/SetFunctionArea.js: -------------------------------------------------------------------------------- 1 | import "./SetFunctionArea.css"; 2 | import { memo, useState } from "react"; 3 | import { Button, message } from "antd"; 4 | import { PlusOutlined, AppstoreAddOutlined } from "@ant-design/icons"; 5 | import { useDispatch, useSelector } from "react-redux"; 6 | import FuncModal from "../../FuncModal/FuncModal"; 7 | import News from "../../News/News"; 8 | import Todo from "../../Todo/Todo"; 9 | import Pictures from "../../Pictures"; 10 | import Notes from "../../Notes"; 11 | import Weather from "../../Weather/Weather"; 12 | import CalComponent from "../../Calendar/CalComponent"; 13 | import Competition from "../../Competition/Competition"; 14 | import CountDown from "../../CountDown/CountDown"; 15 | import ServerMonitor from "../../ServerMonitor/ServerMonitor"; 16 | import ToolKit from "../../ToolKit/ToolKit"; 17 | import Demos from "../../Demos/Demos"; 18 | // import Snippets from '../../Snippets/Snippets' 19 | // import YearToday from '../../FuncModule/YearToday/YearToday'; 20 | import TomatoClock from "../../TomatoClock/TomatoClock"; 21 | import { useEffect } from "react"; 22 | import WoodenFish from "../../WoodenFish/WoodenFish"; 23 | import FormHabit from "../../FormHabit/FormHabit"; 24 | import StockMarket from "../../StockMarket/StockMarket"; 25 | 26 | //FIXME:style have some problem 27 | 28 | const funcs = [ 29 | { id: 0, node: , cover: 2 }, 30 | { id: 1, node: , cover: 2 }, 31 | { id: 2, node: , cover: 2 }, 32 | { id: 3, node: , cover: 1 }, 33 | { id: 4, node: , cover: 2 }, 34 | { id: 5, node: , cover: 2 }, 35 | { id: 6, node: , cover: 2 }, 36 | { id: 7, node: , cover: 2 }, 37 | { id: 8, node: , cover: 2 }, 38 | { id: 9, node: , cover: 2 }, 39 | { id: 10, node: , cover: 2 }, 40 | // {id:11, node:}, 41 | // {id:11, node:}, 42 | { id: 11, node: , cover: 2 }, 43 | { id: 12, node: , cover: 1 }, 44 | { id: 13, node: , cover: 1 }, 45 | { id: 14, node: , cover: 1 }, 46 | ]; 47 | 48 | const SetFunctionArea = memo(() => { 49 | const [isModalVisible, setIsModalVisible] = useState(false); 50 | let timePos = useSelector(state => state.timePos); 51 | const dispatch = useDispatch(); 52 | 53 | const showModal = () => { 54 | setIsModalVisible(true); 55 | }; 56 | 57 | const handleOk = () => { 58 | setIsModalVisible(false); 59 | }; 60 | 61 | const handleCancel = () => { 62 | setIsModalVisible(false); 63 | }; 64 | 65 | let funcNum = 24; 66 | 67 | const addFunc = id => { 68 | let newList = JSON.parse(localStorage.getItem("functionList")); 69 | let cover = newList.reduce((pre, cur) => { 70 | return pre + funcs[cur].cover; 71 | }, 0); 72 | // && cover + 目前要添加的组件cover <= funcNum 限制组件个数 73 | if (newList.indexOf(id) === -1 && cover + funcs[id].cover <= funcNum) { 74 | newList.push(id); 75 | localStorage.setItem("functionList", JSON.stringify(newList)); 76 | dispatch({ 77 | type: "CHANGE_FUNCS", 78 | functionList: newList, 79 | }); 80 | } else { 81 | message.info("功能组件已存在 或 已超过最大放置组件数"); 82 | } 83 | }; 84 | 85 | return ( 86 |
87 | 88 | 89 | 添加功能 90 | 91 | 101 | // 添加 Card 102 | //
103 | // } 104 | closable={false} 105 | visible={isModalVisible} 106 | onOk={handleOk} 107 | onCancel={handleCancel} 108 | > 109 |
119 | {funcs.map((item, index) => { 120 | return ( 121 | //FIXME:天气应用无法显示 122 |
126 |
1 ? "352px" : "165px" }} 129 | > 130 |
137 | {item.node} 138 |
139 | ); 140 | })} 141 |
142 | 143 | 144 | ); 145 | }); 146 | 147 | export { SetFunctionArea, funcs }; 148 | -------------------------------------------------------------------------------- /src/components/MottoFooter/MottoFooter.css: -------------------------------------------------------------------------------- 1 | .footer { 2 | position: absolute; 3 | font-size: 20px; 4 | font-weight: 600; 5 | text-align: center; 6 | left: 50%; 7 | bottom: 1vh; 8 | transform: translateX(-50%); 9 | /* background-color: #000000CC; */ 10 | border-radius: 10px; 11 | padding: 10px 50px; 12 | color: white; 13 | cursor: pointer; 14 | } 15 | .footer:hover { 16 | background-color: #00000055; 17 | } 18 | .footer:hover span { 19 | visibility: visible; 20 | } 21 | .footer span { 22 | visibility: hidden; 23 | font-size: 18px; 24 | } 25 | 26 | .slidein { 27 | animation: slide 1s; 28 | } 29 | 30 | @keyframes slide { 31 | 0% { 32 | opacity: 0; 33 | } 34 | 100% { 35 | opacity: 1; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/components/News/News.css: -------------------------------------------------------------------------------- 1 | /* .news{ 2 | position: absolute; 3 | display: inline-block; 4 | width: 352px; 5 | height: 165px; 6 | width: 18vw; 7 | height: 8.5vw; 8 | margin:20px; 9 | left:764px; 10 | top:20px; 11 | background: #FFFFFF; 12 | box-sizing: border-box; 13 | border-radius: 20px; 14 | transition: .1s ease-in; 15 | } 16 | .news:active{ 17 | transform: scale(0.95); 18 | } */ 19 | .briefNav .left { 20 | display: inline-block; 21 | margin-top: 13px; 22 | } 23 | /* .briefNav .left p{ 24 | 25 | font-style: normal; 26 | font-weight: bold; 27 | font-size: 18px; 28 | line-height: 22px; 29 | 30 | margin: 0; 31 | display: inline-block; 32 | letter-spacing: 0.2rem; 33 | cursor: pointer; 34 | margin-left: 12px; 35 | } 36 | .briefNav .left div{ 37 | display: inline-block; 38 | margin-left: 18px; 39 | width: 6px; 40 | height: 14px; 41 | background: linear-gradient(180deg, #FFC531 14.58%, #FF5631 100%); 42 | box-shadow: 0px 3px 6px rgba(255, 141, 6, 0.8); 43 | border-radius: 20px; 44 | } */ 45 | .right { 46 | /* position: absolute; */ 47 | display: inline-flex; 48 | align-items: center; 49 | justify-content: space-around; 50 | width: 120px; 51 | line-height: 18px; 52 | margin-left: 96px; 53 | cursor: pointer; 54 | } 55 | .right span { 56 | height: 22px; 57 | width: 22px; 58 | font-size: 22px; 59 | } 60 | 61 | .briefList { 62 | /* position: absolute; */ 63 | display: flex; 64 | margin-top: 10px; 65 | flex-direction: column; 66 | flex-wrap: wrap; 67 | justify-content: space-around; 68 | align-content: space-between; 69 | width: 320px; 70 | height: 110px; 71 | margin-left: 21px; 72 | /* top: 40px; 73 | left:50%; 74 | transform:translateX(-50%); */ 75 | } 76 | .briefList .briefListItem { 77 | width: 150px; 78 | height: 18px; 79 | line-height: 16px; 80 | /* font-size: 12px; */ 81 | font-weight: 600; 82 | color: #00000066; 83 | margin: 3px 0; 84 | overflow: hidden; 85 | text-overflow: ellipsis; 86 | white-space: nowrap; 87 | letter-spacing: 1px; 88 | } 89 | .briefList .briefListItem a { 90 | color: #00000066; 91 | } 92 | .briefList .briefListItem a:hover { 93 | color: #000000; 94 | } 95 | .briefList .briefListItem span { 96 | font-size: 16px; 97 | font-weight: 700; 98 | margin-right: 10px; 99 | line-height: 18px; 100 | color: #00000066; 101 | cursor: pointer; 102 | } 103 | .briefList .briefListItem:nth-child(1) span { 104 | color: #ffdd2b; 105 | } 106 | .briefList .briefListItem:nth-child(2) span { 107 | color: #b2b2b2; 108 | } 109 | .briefList .briefListItem:nth-child(3) span { 110 | color: #d2a36c; 111 | } 112 | .line { 113 | margin-left: 123px; 114 | margin-top: -54px; 115 | width: 100px; 116 | height: 0px; 117 | border: 1px solid rgba(0, 0, 0, 0.1); 118 | transform: rotate(90deg); 119 | } 120 | 121 | span .newsItemNum { 122 | display: inline-block; 123 | line-height: 20px; 124 | text-align: center; 125 | border-radius: 3px; 126 | margin-right: 20px; 127 | font-size: 18px; 128 | font-weight: 600; 129 | width: 20px; 130 | height: 20px; 131 | background-color: #e4e0e0; 132 | position: relative; 133 | top: 1.5px; 134 | } 135 | .newsItem:nth-child(1) span .newsItemNum { 136 | background-color: #ffdd2b; 137 | } 138 | .newsItem:nth-child(2) span .newsItemNum { 139 | background-color: #b2b2b2; 140 | } 141 | .newsItem:nth-child(3) span .newsItemNum { 142 | background-color: #d2a36c; 143 | } 144 | 145 | .newsItem > span { 146 | overflow: hidden; 147 | text-overflow: ellipsis; 148 | white-space: nowrap; 149 | width: 450px; 150 | position: relative; 151 | } 152 | 153 | .newsItem:hover { 154 | background-color: #00000033; 155 | transition: 0.3s ease-out; 156 | } 157 | /* 滚动条样式 */ 158 | .newsList::-webkit-scrollbar { 159 | width: 3px; 160 | } 161 | .newsList::-webkit-scrollbar-thumb { 162 | border-radius: 10px; 163 | box-shadow: inset 0 0 4px #00000033; 164 | background: rgba(0, 0, 0, 0.2); 165 | } 166 | .newsList::-webkit-scrollbar-track { 167 | box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); 168 | border-radius: 10px; 169 | background: rgba(0, 0, 0, 0.1); 170 | } 171 | 172 | .ant-tabs-dropdown { 173 | background-color: #fff; 174 | } 175 | -------------------------------------------------------------------------------- /src/components/Notes/index.css: -------------------------------------------------------------------------------- 1 | @import "~antd/dist/antd.css"; 2 | /* .ant-modal-body{ 3 | padding-left: 5px; 4 | padding-right: 5px; 5 | } */ 6 | .memorandum { 7 | display: flex; 8 | height: 100%; 9 | justify-content: space-between; 10 | } 11 | .notes-tabs { 12 | width: 100%; 13 | max-width: 200px; 14 | /* max-height: 500px; */ 15 | height: 100%; 16 | display: flex; 17 | flex-direction: column; 18 | text-align: center; 19 | position: relative; 20 | } 21 | .notes-tabs-body { 22 | height: 100%; 23 | overflow: auto; 24 | background: #f0f1f4; 25 | } 26 | .wrapper { 27 | display: flex; 28 | flex-direction: column; 29 | align-items: center; 30 | justify-content: center; 31 | height: calc(100% - 55px); 32 | } 33 | .ant-card-body { 34 | padding: 24px 12px; 35 | } 36 | .notes-tabs-item { 37 | background: #f0f1f4; 38 | position: relative; 39 | font-size: 14px; 40 | } 41 | .notes-tabs-item:hover .ant-card-meta-title { 42 | color: #69c0ff; 43 | } 44 | .notes-tabs-checked { 45 | background: #fff; 46 | color: #222; 47 | } 48 | 49 | /* 笔记删除按钮 */ 50 | .notes-del { 51 | position: absolute; 52 | top: 0; 53 | right: 0; 54 | } 55 | /* 笔记添加按钮 */ 56 | .notes-add { 57 | position: absolute; 58 | bottom: 10px; 59 | right: 22px; 60 | } 61 | -------------------------------------------------------------------------------- /src/components/Notes/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, memo } from "react"; 2 | import { Button, message } from "antd"; 3 | import MarkdownNotes from "./markdown-notes/index"; 4 | import "./index.css"; 5 | import { PlusOutlined } from "@ant-design/icons"; 6 | import useLocalStorage from "../../hooks/useLocalStorage"; 7 | import NoesTabs from "./noesTabs"; 8 | import { nanoid } from "nanoid"; 9 | import cookie from "react-cookies"; 10 | import FuncCard from "../FuncCard/FuncCard"; 11 | import FuncModal from "../FuncModal/FuncModal"; 12 | 13 | const Notes = () => { 14 | const [notesList, setNotesList] = useLocalStorage("notesList", []); 15 | const [notesData, setNotesData] = useState(notesList); //笔记数据 16 | const [noteIndex, setNoteIndex] = useState(0); // 选中第几个 17 | const [isNotesVisible, setNotesVisible] = useState(false); // 弹框 18 | 19 | useEffect(() => { 20 | async function getNotes() { 21 | fetch("/api/functions/getmynotes/", { 22 | credentials: "include", 23 | }) 24 | .then(response => response.json()) 25 | .then(data => { 26 | setNotesData(JSON.parse(data.res)); 27 | setNotesList(JSON.parse(data.res)); 28 | }) 29 | .catch(e => console.log(e.message)); 30 | } 31 | if (cookie.load("status") === "200") { 32 | //获取数据 33 | getNotes(); 34 | } 35 | }, []); 36 | 37 | // useEffect(()=>{ 38 | // console.log('notes改变') 39 | // let url = defaultSetting.site + '/functions/savemynotes/' 40 | // async function saveNotes(){ 41 | // fetch(url,{ 42 | // method:'post', 43 | // body:JSON.stringify(notesData), 44 | // headers: { 45 | // 'Content-Type': 'application/json' 46 | // }, 47 | // credentials:'include' 48 | // }).then((response)=>response.json()) 49 | // .then((data)=>{ }) 50 | // .catch((e)=>console.log("error")); 51 | // } 52 | // if(cookie.load('status')==='200'){ //保存到数据库 53 | // saveNotes() 54 | // } 55 | // },[notesData]) 56 | 57 | // 是否显示弹框 58 | const isShowModal = () => { 59 | setNotesVisible(!isNotesVisible); 60 | async function saveNotes() { 61 | fetch("/api/functions/savemynotes/", { 62 | method: "post", 63 | body: JSON.stringify(notesData), 64 | headers: { 65 | "Content-Type": "application/json", 66 | }, 67 | credentials: "include", 68 | }) 69 | .then(response => response.json()) 70 | .then(data => {}) 71 | .catch(e => console.log("error")); 72 | } 73 | if (cookie.load("status") === "200" && isNotesVisible) { 74 | //保存到数据库 75 | saveNotes(); 76 | message.success("笔记保存成功"); 77 | } 78 | }; 79 | // 添加笔记 80 | const isNoteAdd = () => { 81 | if (notesData.length === 0) setNoteIndex(0); 82 | const data = notesData.concat([ 83 | { id: nanoid(), value: "无内容", text: "" }, 84 | // 使用nanoid而不是uuid,引用nanoid包 85 | ]); 86 | setNotesData(data); 87 | setNotesList(data); 88 | }; 89 | 90 | return ( 91 | <> 92 | 101 | {/*
102 |
103 |

笔记

104 |
*/} 105 |
106 |
{notesData[0]?.value}
107 |
{notesData[1]?.value}
108 |
{notesData[2]?.value}
109 |
{notesData[3]?.value}
110 |
111 |
112 | 122 | 笔记 123 | 124 | } 125 | width={1000} 126 | footer={null} 127 | visible={isNotesVisible} 128 | onCancel={isShowModal} 129 | > 130 |
131 |
132 |
133 | {notesData.length !== 0 ? ( 134 | notesData.map((item, index) => { 135 | return ( 136 | 146 | ); 147 | }) 148 | ) : ( 149 |

快来添加第一条笔记吧

150 | )} 151 |
152 |
160 | 166 |
167 |
168 | 169 | ); 170 | }; 171 | 172 | export default memo(Notes); 173 | -------------------------------------------------------------------------------- /src/components/Notes/markdown-notes/index.css: -------------------------------------------------------------------------------- 1 | .button-type-image, 2 | .button-type-fullscreen { 3 | display: none !important; 4 | } 5 | -------------------------------------------------------------------------------- /src/components/Notes/markdown-notes/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import MarkdownIt from "markdown-it"; 3 | import MdEditor from "react-markdown-editor-lite"; 4 | // 导入编辑器的样式 5 | import "react-markdown-editor-lite/lib/index.css"; 6 | // markdown代码颜色 7 | import hljs from "highlight.js"; 8 | import "highlight.js/styles/atom-one-light.css"; 9 | // 编辑样式 10 | import "./index.css"; 11 | 12 | // 初始化Markdown解析器 13 | const mdParser = new MarkdownIt({ 14 | html: true, 15 | linkify: true, 16 | typographer: true, 17 | highlight: (str, lang) => { 18 | if (lang && hljs.getLanguage(lang)) { 19 | try { 20 | return hljs.highlight(lang, str).value; 21 | } catch (_) { 22 | console.log("出错了"); 23 | } 24 | } 25 | return ""; // use external default escaping 26 | }, 27 | }); 28 | 29 | const MarkdownNotes = noteData => { 30 | const [notesValue, setIsCardDelete] = useState(""); 31 | 32 | // 初始化更新 33 | React.useEffect(() => { 34 | const newNoteData = noteData?.notesData[noteData.noteIndex] 35 | ? noteData?.notesData[noteData.noteIndex] 36 | : noteData?.notesData[noteData.noteIndex - 1]; 37 | setIsCardDelete(newNoteData); 38 | return () => {}; 39 | }, [noteData.notesData, noteData.noteIndex]); 40 | 41 | // 使用正则表达式从字符串中删除 HTML/XML 标记。 42 | const stripHTMLTags = str => str.replace(/<[^>]*>/g, ""); 43 | 44 | // 改变markdown笔记内容时触发 45 | const handleEditorChange = ({ html, text }) => { 46 | setIsCardDelete(text); 47 | const note = noteData.notesData; 48 | note[noteData.noteIndex].text = text; 49 | note[noteData.noteIndex].value = stripHTMLTags(html); 50 | noteData.setNotesData(note); 51 | noteData.setNotesList(note); 52 | }; 53 | 54 | return ( 55 | <> 56 | {noteData?.notesData.length !== 0 ? ( 57 | mdParser.render(text)} 63 | onChange={handleEditorChange} 64 | /> 65 | ) : ( 66 |
81 | 暂无笔记 82 |
83 | )} 84 | 85 | ); 86 | }; 87 | 88 | export default MarkdownNotes; 89 | -------------------------------------------------------------------------------- /src/components/Notes/noesTabs.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Button, Card, Modal } from "antd"; 3 | import { DeleteOutlined, ExclamationCircleOutlined } from "@ant-design/icons"; 4 | import Meta from "antd/es/card/Meta"; 5 | 6 | const NoesTabs = data => { 7 | const [isCardDelete, setIsCardDelete] = useState(false); // 删除按钮 8 | 9 | // 是否显示删除按钮 10 | const handleMouse = flag => { 11 | setIsCardDelete(flag); 12 | }; 13 | // 选中当前笔记 14 | const isShowNotes = (item, index) => { 15 | data.setNoteIndex(index); 16 | }; 17 | // 删除笔记 18 | const isDelete = (item, index) => { 19 | Modal.confirm({ 20 | title: "提示", 21 | icon: , 22 | content: "删除这条笔记?", 23 | okText: "确认", 24 | cancelText: "取消", 25 | onOk: function () { 26 | const newNotesData = data.notesData.filter(i => i.id !== item.id); 27 | data.setNotesData(newNotesData); 28 | data.setNotesList(newNotesData); 29 | data.setNoteIndex(index - 1); 30 | }, 31 | onCancel: function () { 32 | console.log("取消"); 33 | }, 34 | }); 35 | }; 36 | return ( 37 | <> 38 | handleMouse(true)} 45 | onMouseLeave={() => handleMouse(false)} 46 | onClick={() => isShowNotes(data.item, data.index)} 47 | hoverable={true} 48 | > 49 | 50 | 92 | ); 93 | }, 94 | }, 95 | ]; 96 | 97 | const settings = { 98 | dots: true, 99 | fade: true, 100 | speed: 500, 101 | autoplay: true, 102 | autoplaySpeed: 4000, //间隔 103 | }; 104 | 105 | const props = { 106 | name: "file", 107 | withCredentials: true, 108 | action: "/api/img/uploadmyimg/", 109 | onChange(info) { 110 | const { status, response } = info.file; 111 | if (status !== "uploading") { 112 | console.log(info.file, info.fileList); 113 | } 114 | if (status === "done") { 115 | message.success(`${info.file.name} file uploaded successfully`); 116 | setMyImgList(response.objectList); 117 | localStorage.setItem("myimglist", JSON.stringify(response.objectList)); 118 | } else if (info.file.status === "error") { 119 | message.error(`${info.file.name} file upload failed.`); 120 | } 121 | }, 122 | }; 123 | 124 | return ( 125 | <> 126 |
127 | 128 | 129 | { 130 | // picData.map(() => { }) 131 | myimglist.map((item, index) => { 132 | let url = "/pic/" + item; 133 | return ( 134 |
135 | {""} 143 |
144 | ); 145 | }) 146 | } 147 |
148 |
149 |
150 | 160 | 图片墙 161 | 162 | } 163 | visible={modalVisible} 164 | onCancel={() => { 165 | setModalVisible(false); 166 | }} 167 | footer={null} 168 | > 169 | 176 | 177 | 185 | 186 | 187 | 188 | ); 189 | }; 190 | 191 | export default memo(Pictures); 192 | -------------------------------------------------------------------------------- /src/components/Pictures/style.css: -------------------------------------------------------------------------------- 1 | @import "~antd/dist/antd.css"; 2 | /* .Picture{ 3 | transition: all .5s ; 4 | } */ 5 | .carousel { 6 | /* position: relative; 7 | z-index: 1; */ 8 | /* top:20px; 9 | left: 1136px; */ 10 | /* width: 18vw; 11 | height: 8.5vw; */ 12 | width: 352px; 13 | height: 165px; 14 | border: 0 solid white; 15 | border-radius: 20px; 16 | overflow: hidden; 17 | /* transition: all .5s ; */ 18 | } 19 | .carousel_func { 20 | background-color: #00000000; 21 | } 22 | .carousel:active { 23 | /* transform: scale(0.95); */ 24 | } 25 | 26 | .panel { 27 | width: 100%; 28 | /* padding-bottom: 60%; 29 | height: 0; */ 30 | /* position: relative; 31 | z-index: 100; */ 32 | color: #ffffff; 33 | text-align: center; 34 | background: #364d79; 35 | } 36 | 37 | /* 滚动条样式 */ 38 | div::-webkit-scrollbar { 39 | width: 3px; 40 | } 41 | div::-webkit-scrollbar-thumb { 42 | border-radius: 10px; 43 | box-shadow: inset 0 0 4px #00000033; 44 | background: rgba(0, 0, 0, 0.2); 45 | } 46 | div::-webkit-scrollbar-track { 47 | box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); 48 | border-radius: 10px; 49 | background: rgba(0, 0, 0, 0.1); 50 | } 51 | -------------------------------------------------------------------------------- /src/components/Search/Search.css: -------------------------------------------------------------------------------- 1 | /* 搜索框 */ 2 | .search { 3 | width: 40vw; 4 | height: 6vh; 5 | position: absolute; 6 | left: 50%; 7 | transform: translateX(-50%); 8 | /* border: 1px solid red; */ 9 | z-index: 2; 10 | /* margin-top: 3vh; */ 11 | min-width: 600px; 12 | min-height: 50px; 13 | background-color: #ffffffcc; 14 | border-radius: 50px; 15 | transition: 0.3s ease-in-out; 16 | } 17 | .engine { 18 | display: inline-flex; 19 | align-items: center; 20 | justify-content: space-evenly; 21 | padding: 0; 22 | outline: none; 23 | border: none; 24 | width: 4vw; 25 | min-width: 80px; 26 | height: 6vh; 27 | min-height: 50px; 28 | cursor: pointer; 29 | line-height: 6vh; 30 | border-radius: 50px 0 0 50px; 31 | background-color: #ffffffcc; 32 | /* transition:0.5s; */ 33 | } 34 | 35 | .engine:hover { 36 | background-color: #00000026; 37 | } 38 | .engine:focus + .engineList { 39 | display: flex; 40 | } 41 | .engineList { 42 | display: none; 43 | position: absolute; 44 | z-index: 3; 45 | margin-top: 6px; 46 | padding: 5px; 47 | /* width:126px; */ 48 | min-width: 96px; 49 | border-radius: 10px; 50 | background-color: #ffffffeb; 51 | box-shadow: 4px 6px 8px rgba(0, 0, 0, 0.32), 52 | inset 0px 4px 4px rgba(255, 255, 255, 0.2); 53 | transition: 0.2s; 54 | } 55 | .engineList li { 56 | display: flex; 57 | justify-content: space-around; 58 | align-items: center; 59 | height: 6vh; 60 | min-height: 36px; 61 | width: 100px; 62 | margin: 2px 2px; 63 | list-style: none; 64 | color: rgba(0, 0, 0, 0.6); 65 | line-height: 6vh; 66 | border-radius: 10px; 67 | font-size: 2vh; 68 | font-weight: 500; 69 | cursor: pointer; 70 | } 71 | .engineList li span { 72 | display: block; 73 | color: rgba(0, 0, 0, 0.6); 74 | } 75 | .engineList li:hover { 76 | background-color: #0000001a; 77 | } 78 | .search input { 79 | /* font-family: Microsoft YaHei ; */ 80 | color: #0c0c0c; 81 | display: inline-block; 82 | position: absolute; 83 | font-size: 1.2rem; 84 | height: 6vh; 85 | min-height: 50px; 86 | width: 30vw; 87 | outline: none; 88 | border: none; 89 | padding: 0 0 0 10px; 90 | background-color: #ffffff00; 91 | } 92 | .search > span { 93 | display: inline-flex; 94 | position: absolute; 95 | justify-content: center; 96 | align-items: center; 97 | color: #302f2fcc; 98 | border-radius: 50%; 99 | height: 6vh; 100 | min-height: 50px; 101 | width: 6vh; 102 | min-width: 50px; 103 | right: 0; 104 | } 105 | .search > span:hover { 106 | background-color: #00000026; 107 | } 108 | .search input:focus + .presearch-list { 109 | display: block; 110 | } 111 | .presearch-list { 112 | width: 100%; 113 | background: #fffd; 114 | border-radius: 10px; 115 | display: none; 116 | position: absolute; 117 | /* backdrop-filter: blur(20px); */ 118 | overflow: hidden; 119 | margin-top: 6px; 120 | transition: 0.3s ease-in-out; 121 | } 122 | 123 | .presearch-list div { 124 | font-size: 20px; 125 | padding: 2px; 126 | padding-left: 10px; 127 | font-weight: 600; 128 | transition: 0.3s ease-out; 129 | cursor: pointer; 130 | display: flex; 131 | align-items: center; 132 | overflow: hidden; 133 | text-overflow: ellipsis; 134 | white-space: nowrap; 135 | } 136 | .presearch-list > div div { 137 | width: 50%; 138 | /* overflow: hidden; */ 139 | } 140 | .presearch-list > div:hover { 141 | background-color: #0003; 142 | } 143 | .pre-hover { 144 | background-color: #0003; 145 | } 146 | .pre-div-hover { 147 | margin-left: calc(50% - 20px); 148 | transform: translateX(-50%); 149 | display: inline-block; 150 | text-align: center; 151 | } 152 | .presearch-list div:hover div { 153 | margin-left: calc(50% - 20px); 154 | transform: translateX(-50%); 155 | display: inline-block; 156 | text-align: center; 157 | } 158 | .search-history-delete { 159 | position: absolute; 160 | color: #0008; 161 | font-size: 16px; 162 | right: 1%; 163 | opacity: 0; 164 | transition: 0.2s ease-in-out; 165 | } 166 | .presearch-list div:hover .search-history-delete { 167 | opacity: 1; 168 | } 169 | -------------------------------------------------------------------------------- /src/components/ServerMonitor/ServerMonitor.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/src/components/ServerMonitor/ServerMonitor.css -------------------------------------------------------------------------------- /src/components/ServerMonitor/ServerMonitor.js: -------------------------------------------------------------------------------- 1 | import "./ServerMonitor.css"; 2 | import FuncCard from "../FuncCard/FuncCard"; 3 | import { memo, useEffect, useState } from "react"; 4 | import md5 from "js-md5"; 5 | 6 | const ServerMonitor = () => { 7 | const [type, setType] = useState(0); 8 | 9 | const handleChangeType = value => { 10 | setType(value); 11 | }; 12 | 13 | const request_time = new Date().getTime(); 14 | const api_sk = "2tIiizP1DahyBmypN00hqdkdV9OaooI6"; 15 | let request_token = md5(request_time.toString() + md5(api_sk)); 16 | let args = { 17 | request_token: request_token, 18 | request_time: request_time, 19 | }; 20 | 21 | useEffect(() => { 22 | // fetch("http://121.196.148.27:8899/system?action=GetNetWork",{ 23 | // method:'POST', 24 | // body:args, 25 | // credentials:'include' 26 | // }).then((res)=>{ 27 | // console.log(res.data) 28 | // }) 29 | }, []); 30 | 31 | return ( 32 | 41 |
53 | 待完成API接口 54 |
55 |
56 | ); 57 | }; 58 | 59 | export default memo(ServerMonitor); 60 | -------------------------------------------------------------------------------- /src/components/SetBackground/SetBackground.css: -------------------------------------------------------------------------------- 1 | .backgroundImg { 2 | display: flex; 3 | background-size: cover; 4 | width: 100%; 5 | height: 180px; 6 | margin-bottom: 25px; 7 | justify-content: center; 8 | align-items: center; 9 | transition: background-image 0.3s ease-in-out; 10 | } 11 | .backgroundImg div { 12 | text-align: center; 13 | width: 100px; 14 | height: 50px; 15 | color: white; 16 | background-color: #00000088; 17 | border-radius: 10px; 18 | cursor: pointer; 19 | line-height: 50px; 20 | transition: 0.3s; 21 | } 22 | .backgroundImg div:hover { 23 | background-color: #000000dd; 24 | } 25 | .setBackground { 26 | user-select: none; 27 | } 28 | .setBackground span { 29 | margin-left: 10px; 30 | } 31 | .setBackground > div:nth-child(4) span { 32 | margin-left: 0px; 33 | } 34 | .backgroundCover, 35 | .backgroundBlur { 36 | width: 70%; 37 | display: inline-block; 38 | } 39 | 40 | .showBackground { 41 | font-size: 0; 42 | overflow-y: scroll; 43 | height: 550px; 44 | width: 748px; 45 | margin-bottom: 10px; 46 | } 47 | 48 | .showBackgroundItem { 49 | display: inline-flex; 50 | justify-content: center; 51 | align-items: center; 52 | position: relative; 53 | border-radius: 10px; 54 | margin-top: 10px; 55 | margin-bottom: 5px; 56 | margin-right: 5px; 57 | margin-left: 5px; 58 | width: 235px; 59 | height: 120px; 60 | } 61 | .showBackgroundItem div { 62 | position: relative; 63 | width: 50px; 64 | height: 50px; 65 | color: white; 66 | font-size: 20px; 67 | border-radius: 50%; 68 | text-align: center; 69 | opacity: 0; 70 | background-color: #000000cc; 71 | line-height: 50px; 72 | transition: 0.3s ease-in; 73 | cursor: pointer; 74 | } 75 | .showBackgroundItem::before { 76 | content: "hello"; 77 | width: 235px; 78 | height: 120px; 79 | position: absolute; 80 | top: 0px; 81 | right: 0px; 82 | background-color: #00000000; 83 | border-radius: inherit; 84 | transition: 0.3s ease-in-out; 85 | } 86 | .showBackgroundItem:hover::before { 87 | background-color: #00000088; 88 | } 89 | .showBackgroundItem:hover div { 90 | opacity: 1; 91 | } 92 | .showBackgroundItem div:hover { 93 | background-color: rgba(145, 241, 145, 0.8); 94 | } 95 | .showBackgroundItem div:active { 96 | transform: scale(0.8); 97 | } 98 | 99 | .backTab { 100 | font-size: 18px; 101 | letter-spacing: 2px; 102 | font-weight: 700; 103 | line-height: 30px; 104 | display: inline-block; 105 | height: 30px; 106 | } 107 | 108 | /* 滚动条样式 */ 109 | .showBackground::-webkit-scrollbar { 110 | width: 3px; 111 | } 112 | .showBackground::-webkit-scrollbar-thumb { 113 | border-radius: 10px; 114 | box-shadow: inset 0 0 4px #00000033; 115 | background: rgba(0, 0, 0, 0.2); 116 | } 117 | .showBackground::-webkit-scrollbar-track { 118 | box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); 119 | border-radius: 10px; 120 | background: rgba(0, 0, 0, 0.1); 121 | } 122 | -------------------------------------------------------------------------------- /src/components/Snippets/Snippets.css: -------------------------------------------------------------------------------- 1 | .snippets { 2 | height: 200px; 3 | box-sizing: border-box; 4 | padding: 16px; 5 | border: none; 6 | border-radius: 10px; 7 | box-shadow: 0 0 7px rgba(0, 0, 0, 0.15); 8 | resize: none; 9 | background-color: #fffa; 10 | font-family: "SimHei"; 11 | font-size: 16px; 12 | font-weight: 800; 13 | letter-spacing: 1.5px; 14 | } 15 | -------------------------------------------------------------------------------- /src/components/Snippets/Snippets.js: -------------------------------------------------------------------------------- 1 | import "./Snippets.css"; 2 | import { memo } from "react"; 3 | import React from "react"; 4 | import { useSelector, useDispatch } from "react-redux"; 5 | import { SnippetsOutlined } from "@ant-design/icons"; 6 | import "braft-editor/dist/index.css"; 7 | import BraftEditor from "braft-editor"; 8 | // 富文本表情包扩展 9 | // import 'braft-extensions/dist/emoticon.css' 10 | // import Emoticon, { defaultEmoticons } from 'braft-extensions/dist/emoticon' 11 | // import 'braft-extensions/dist/code-highlighter.css' 12 | // import CodeHighlighter from 'braft-extensions/dist/code-highlighter' 13 | 14 | // BraftEditor.use(CodeHighlighter({ 15 | // includeEditors: ['editor-with-code-highlighter'], 16 | // // theme: 'light' // 支持dark和light两种主题,默认为dark 17 | // })) 18 | // BraftEditor.use(CodeHighlighter({ 19 | // includeEditors: ['editor-with-code-highlighter'], 20 | // })) 21 | 22 | const Snippets = memo(props => { 23 | const { id } = props; 24 | const dispatch = useDispatch(); 25 | const snippets = useSelector(state => state.snippets); 26 | // const [editorState,setEditorState] = useState(BraftEditor.createEditorState(null)) 27 | 28 | const deleteSnippet = () => { 29 | let new_snippets = snippets.reduce((pre, cur) => { 30 | if (cur.id !== id) { 31 | pre.push(cur); 32 | } 33 | return pre; 34 | }, []); 35 | dispatch({ 36 | type: "CHANGE_SNIPPETS", 37 | snippets: new_snippets, 38 | }); 39 | }; 40 | 41 | //富文本编辑器 42 | // const handleChange = (editorState) => { 43 | // console.log(editorState) 44 | // setEditorState({ editorState }) 45 | // } 46 | //自定义富文本控件 47 | //富选框参数 48 | // const controls = ['undo', 'redo', 'separator', 49 | // 'font-size', 'line-height', 'letter-spacing', 'separator', 50 | // 'text-color', 'blod', 'italic', 'underline', 'strike-through', 'separator', 51 | // 'superscript', 'subscript', 'remove-styles', 'emoji', 'separator', 'text-indent', 'text-align', 'separator', 52 | // 'headings', 'list-ul', 'list-ol', 'blockquote', 'code', 'separator', 53 | // 'link', 'separator', 'hr', 'separator', 54 | // 'clear', 'separator', 55 | // ] 56 | 57 | const controls = [ 58 | "bold", 59 | "text-color", 60 | "font-size", 61 | "separator", 62 | "list-ul", 63 | "emoji", 64 | "separator", 65 | "clear", 66 | ]; 67 | 68 | return ( 69 |
78 | {/* */} 80 | {/* */} 81 | 94 |
95 | ); 96 | }); 97 | 98 | const SnippetsInMenu = memo(() => { 99 | const dispatch = useDispatch(); 100 | const snippets = useSelector(state => state.snippets); 101 | 102 | const addSnippet = () => { 103 | //TODO:ID改成唯一性的uuid 104 | let new_id = (Math.random() * 1000000).toFixed(0); 105 | let new_snippets = snippets.concat({ id: new_id, content: "" }); 106 | dispatch({ 107 | type: "CHANGE_SNIPPETS", 108 | snippets: new_snippets, 109 | }); 110 | }; 111 | 112 | return ( 113 |
114 | 115 | 116 | 便利贴 117 | 118 |
119 | ); 120 | }); 121 | 122 | export { SnippetsInMenu, Snippets }; 123 | -------------------------------------------------------------------------------- /src/components/StockMarket/StockMarket.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wisdompandamaster/NewTab/32294c506fc87eb0f0048f269d218e28b1659b8d/src/components/StockMarket/StockMarket.css -------------------------------------------------------------------------------- /src/components/StockMarket/StockMarket.js: -------------------------------------------------------------------------------- 1 | import { memo } from "react"; 2 | import FuncCard from "../FuncCard/FuncCard"; 3 | 4 | function StockMarket() { 5 | return ( 6 | 14 | ); 15 | } 16 | 17 | export default memo(StockMarket); 18 | -------------------------------------------------------------------------------- /src/components/SwiperAera/SwiperAera.css: -------------------------------------------------------------------------------- 1 | .swiperaera { 2 | position: absolute; 3 | /* top: 45vh; */ 4 | left: 50%; 5 | transform: translateX(-50%); 6 | width: 85vw; 7 | /* height: 22vw; */ 8 | margin: 0 auto; 9 | /* z-index: 999; */ 10 | border: 0px solid black; 11 | overflow: visible; 12 | border-radius: 20px; 13 | /* height: 50vh; */ 14 | user-select: none; 15 | transition: 0.3s ease-in-out; 16 | } 17 | .fade_in { 18 | animation: fadein 0.5s ease-in-out; 19 | } 20 | 21 | @keyframes fadein { 22 | 0% { 23 | opacity: 0; 24 | } 25 | 100% { 26 | opacity: 1; 27 | } 28 | } 29 | 30 | .swiper-no-swiping .swiper-pagination-bullet { 31 | width: 50px; 32 | height: 25px; 33 | text-align: center; 34 | line-height: 25px; 35 | font-size: 12px; 36 | font-weight: 600; 37 | letter-spacing: 0.1rem; 38 | color: #000; 39 | opacity: 1; 40 | border-radius: 5px; 41 | background: rgba(0, 0, 0, 0.2); 42 | } 43 | 44 | .swiper-no-swiping .swiper-pagination-bullet-active { 45 | color: #fff; 46 | background: rgba(0, 0, 0, 0.5); 47 | } 48 | -------------------------------------------------------------------------------- /src/components/SwiperAera/SwiperAera.js: -------------------------------------------------------------------------------- 1 | import "./SwiperAera.css"; 2 | import { Swiper, SwiperSlide } from "swiper/react"; 3 | import "swiper/css"; 4 | import "swiper/css/pagination"; 5 | import { useSelector } from "react-redux"; 6 | import { Pagination, Mousewheel } from "swiper"; 7 | import FunctionAera from "../FunctionAera/FunctionAera"; 8 | import Apps from "../Apps/Apps"; 9 | import { memo } from "react"; 10 | 11 | const SwiperAera = () => { 12 | const clear = useSelector(state => state.clear); 13 | // const timePos = useSelector(state => state.timePos); 14 | let display = clear ? "none" : "block"; 15 | let top = "25vh"; 16 | let slideType = ["链接", "组件"]; 17 | const pagination = { 18 | clickable: true, 19 | renderBullet: (index, className) => { 20 | return '' + slideType[index] + ""; 21 | }, 22 | }; 23 | 24 | return ( 25 |
29 | {/* 加swiper-no-swipping可以让swiper不能拖动滑动 */} 30 | console.log("slide change")} 37 | // onSwiper={swiper => console.log(swiper)} 38 | pagination={pagination} 39 | //scrollbar={{ draggable: false }} 40 | mousewheel={true} 41 | modules={[Pagination, Mousewheel]} 42 | > 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | ); 52 | }; 53 | 54 | export default memo(SwiperAera); //memo防止由父组件引发的不必要重复渲染 55 | -------------------------------------------------------------------------------- /src/components/Todo/AddItemInput/index.css: -------------------------------------------------------------------------------- 1 | #add-item-input { 2 | padding: 1em; 3 | background-color: aliceblue; 4 | display: flex; 5 | align-items: center; 6 | margin-bottom: 2%; 7 | border-radius: 10px; 8 | position: absolute; 9 | width: 97%; 10 | z-index: 3; 11 | } 12 | 13 | #add-item-input #plus-icon, 14 | #add-item-input input { 15 | display: block; 16 | width: 100%; /* vertical-align: top; */ 17 | } 18 | 19 | #add-item-input #plus-icon { 20 | height: 1em; 21 | width: 1em; 22 | margin-right: 1em; 23 | } 24 | 25 | #add-item-input #plus-icon path { 26 | fill: gray; 27 | } 28 | 29 | #add-item-input input { 30 | border: 0px; 31 | color: gray; 32 | font-size: 1em; 33 | font-weight: 400; 34 | height: 1em; 35 | outline: 0px; 36 | padding: 0px; 37 | background-color: #00000000; 38 | } 39 | -------------------------------------------------------------------------------- /src/components/Todo/AddItemInput/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { PlusOutlined } from "@ant-design/icons"; 3 | import "./index.css"; 4 | import { nanoid } from "nanoid"; 5 | 6 | function AddItemInput({ onSubmit }) { 7 | const [input, setInput] = useState(""); 8 | 9 | const handleChange = e => { 10 | setInput(e.target.value); 11 | }; 12 | 13 | const handleKeyDown = e => { 14 | if (e.key === "Enter") { 15 | onSubmit({ 16 | id: nanoid(), 17 | isCompleted: false, 18 | text: input, 19 | date: new Date().toLocaleDateString(), 20 | }); 21 | setInput(""); 22 | } 23 | }; 24 | 25 | return ( 26 |
27 | 28 | 35 |
36 | ); 37 | } 38 | 39 | export default AddItemInput; 40 | -------------------------------------------------------------------------------- /src/components/Todo/EditItemInput.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from "react"; 2 | import DatePicker from "react-datepicker"; 3 | import "react-datepicker/dist/react-datepicker.css"; 4 | 5 | function EditItemInput({ date, edit, onSubmit }) { 6 | const [input, setInput] = useState(edit ? edit.value : ""); 7 | const [startDate, setStartDate] = useState(new Date(date)); 8 | const textRef = useRef(); 9 | 10 | useEffect(() => { 11 | textRef.current.focus(); 12 | textRef.current.setSelectionRange(-1, -1); 13 | }, []); 14 | 15 | const editText = e => { 16 | setInput(e.target.value); 17 | }; 18 | 19 | const changeDate = date => { 20 | setStartDate(date); 21 | onSubmit({ 22 | id: edit.id, 23 | isCompleted: false, 24 | text: input, 25 | date: date.toLocaleDateString(), 26 | }); 27 | }; 28 | 29 | const handleKeyDown = e => { 30 | if (e.key === "Enter") { 31 | onSubmit({ 32 | id: edit.id, 33 | isCompleted: false, 34 | text: input, 35 | date: startDate.toLocaleDateString(), 36 | }); 37 | setInput(""); 38 | } 39 | }; 40 | 41 | return ( 42 |
43 |