ssq页面
Change-Id: I11f58d72d9e33eae9ec84f46473fa6144b480501
diff --git a/front/README.md b/front/README.md
deleted file mode 100644
index 58beeac..0000000
--- a/front/README.md
+++ /dev/null
@@ -1,70 +0,0 @@
-# Getting Started with Create React App
-
-This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
-
-## Available Scripts
-
-In the project directory, you can run:
-
-### `npm start`
-
-Runs the app in the development mode.\
-Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
-
-The page will reload when you make changes.\
-You may also see any lint errors in the console.
-
-### `npm test`
-
-Launches the test runner in the interactive watch mode.\
-See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
-
-### `npm run build`
-
-Builds the app for production to the `build` folder.\
-It correctly bundles React in production mode and optimizes the build for the best performance.
-
-The build is minified and the filenames include the hashes.\
-Your app is ready to be deployed!
-
-See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
-
-### `npm run eject`
-
-**Note: this is a one-way operation. Once you `eject`, you can't go back!**
-
-If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
-
-Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
-
-You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
-
-## Learn More
-
-You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
-
-To learn React, check out the [React documentation](https://reactjs.org/).
-
-### Code Splitting
-
-This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
-
-### Analyzing the Bundle Size
-
-This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
-
-### Making a Progressive Web App
-
-This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
-
-### Advanced Configuration
-
-This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
-
-### Deployment
-
-This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
-
-### `npm run build` fails to minify
-
-This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
diff --git a/front/package-lock.json b/front/package-lock.json
index 2fc8ceb..0adbc29 100644
--- a/front/package-lock.json
+++ b/front/package-lock.json
@@ -16,6 +16,7 @@
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^13.5.0",
+ "antd": "^5.25.4",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.30.1",
@@ -54,6 +55,121 @@
"node": ">=6.0.0"
}
},
+ "node_modules/@ant-design/colors": {
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-7.2.1.tgz",
+ "integrity": "sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/fast-color": "^2.0.6"
+ }
+ },
+ "node_modules/@ant-design/cssinjs": {
+ "version": "1.23.0",
+ "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.23.0.tgz",
+ "integrity": "sha512-7GAg9bD/iC9ikWatU9ym+P9ugJhi/WbsTWzcKN6T4gU0aehsprtke1UAaaSxxkjjmkJb3llet/rbUSLPgwlY4w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "@emotion/hash": "^0.8.0",
+ "@emotion/unitless": "^0.7.5",
+ "classnames": "^2.3.1",
+ "csstype": "^3.1.3",
+ "rc-util": "^5.35.0",
+ "stylis": "^4.3.4"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/@ant-design/cssinjs-utils": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.3.tgz",
+ "integrity": "sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/cssinjs": "^1.21.0",
+ "@babel/runtime": "^7.23.2",
+ "rc-util": "^5.38.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@ant-design/cssinjs/node_modules/@emotion/hash": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
+ "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==",
+ "license": "MIT"
+ },
+ "node_modules/@ant-design/cssinjs/node_modules/@emotion/unitless": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
+ "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==",
+ "license": "MIT"
+ },
+ "node_modules/@ant-design/cssinjs/node_modules/stylis": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz",
+ "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==",
+ "license": "MIT"
+ },
+ "node_modules/@ant-design/fast-color": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-2.0.6.tgz",
+ "integrity": "sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=8.x"
+ }
+ },
+ "node_modules/@ant-design/icons": {
+ "version": "5.6.1",
+ "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.6.1.tgz",
+ "integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/colors": "^7.0.0",
+ "@ant-design/icons-svg": "^4.4.0",
+ "@babel/runtime": "^7.24.8",
+ "classnames": "^2.2.6",
+ "rc-util": "^5.31.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/@ant-design/icons-svg": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz",
+ "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==",
+ "license": "MIT"
+ },
+ "node_modules/@ant-design/react-slick": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.1.2.tgz",
+ "integrity": "sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.4",
+ "classnames": "^2.2.5",
+ "json2mq": "^0.2.0",
+ "resize-observer-polyfill": "^1.5.1",
+ "throttle-debounce": "^5.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0"
+ }
+ },
"node_modules/@babel/code-frame": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
@@ -3499,6 +3615,155 @@
"url": "https://opencollective.com/popperjs"
}
},
+ "node_modules/@rc-component/async-validator": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.0.4.tgz",
+ "integrity": "sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.24.4"
+ },
+ "engines": {
+ "node": ">=14.x"
+ }
+ },
+ "node_modules/@rc-component/color-picker": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-2.0.1.tgz",
+ "integrity": "sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/fast-color": "^2.0.6",
+ "@babel/runtime": "^7.23.6",
+ "classnames": "^2.2.6",
+ "rc-util": "^5.38.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/context": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/context/-/context-1.4.0.tgz",
+ "integrity": "sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "rc-util": "^5.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/mini-decimal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz",
+ "integrity": "sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ }
+ },
+ "node_modules/@rc-component/mutate-observer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz",
+ "integrity": "sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.0",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.24.4"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/portal": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz",
+ "integrity": "sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.0",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.24.4"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/qrcode": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.0.0.tgz",
+ "integrity": "sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.24.7",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.38.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/tour": {
+ "version": "1.15.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-1.15.1.tgz",
+ "integrity": "sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.0",
+ "@rc-component/portal": "^1.0.0-9",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.24.4"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/@rc-component/trigger": {
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.2.6.tgz",
+ "integrity": "sha512-/9zuTnWwhQ3S3WT1T8BubuFTT46kvnXgaERR9f4BTKyn61/wpf/BvbImzYBubzJibU707FxwbKszLlHjcLiv1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.2",
+ "@rc-component/portal": "^1.1.0",
+ "classnames": "^2.3.2",
+ "rc-motion": "^2.0.0",
+ "rc-resize-observer": "^1.3.1",
+ "rc-util": "^5.44.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
"node_modules/@remix-run/router": {
"version": "1.23.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz",
@@ -4998,6 +5263,71 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "node_modules/antd": {
+ "version": "5.25.4",
+ "resolved": "https://registry.npmjs.org/antd/-/antd-5.25.4.tgz",
+ "integrity": "sha512-yXdWqq1NJSZnD1HoPZWnWuQJGVYYnB3h0Ufsz4sbt3T0N9SdJ4G9GPpLMk8Gn9zWtwBekfR4THPVZ9uzAyhBHQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@ant-design/colors": "^7.2.1",
+ "@ant-design/cssinjs": "^1.23.0",
+ "@ant-design/cssinjs-utils": "^1.1.3",
+ "@ant-design/fast-color": "^2.0.6",
+ "@ant-design/icons": "^5.6.1",
+ "@ant-design/react-slick": "~1.1.2",
+ "@babel/runtime": "^7.26.0",
+ "@rc-component/color-picker": "~2.0.1",
+ "@rc-component/mutate-observer": "^1.1.0",
+ "@rc-component/qrcode": "~1.0.0",
+ "@rc-component/tour": "~1.15.1",
+ "@rc-component/trigger": "^2.2.6",
+ "classnames": "^2.5.1",
+ "copy-to-clipboard": "^3.3.3",
+ "dayjs": "^1.11.11",
+ "rc-cascader": "~3.34.0",
+ "rc-checkbox": "~3.5.0",
+ "rc-collapse": "~3.9.0",
+ "rc-dialog": "~9.6.0",
+ "rc-drawer": "~7.3.0",
+ "rc-dropdown": "~4.2.1",
+ "rc-field-form": "~2.7.0",
+ "rc-image": "~7.12.0",
+ "rc-input": "~1.8.0",
+ "rc-input-number": "~9.5.0",
+ "rc-mentions": "~2.20.0",
+ "rc-menu": "~9.16.1",
+ "rc-motion": "^2.9.5",
+ "rc-notification": "~5.6.4",
+ "rc-pagination": "~5.1.0",
+ "rc-picker": "~4.11.3",
+ "rc-progress": "~4.0.0",
+ "rc-rate": "~2.13.1",
+ "rc-resize-observer": "^1.4.3",
+ "rc-segmented": "~2.7.0",
+ "rc-select": "~14.16.8",
+ "rc-slider": "~11.1.8",
+ "rc-steps": "~6.0.1",
+ "rc-switch": "~4.1.0",
+ "rc-table": "~7.50.5",
+ "rc-tabs": "~15.6.1",
+ "rc-textarea": "~1.10.0",
+ "rc-tooltip": "~6.4.0",
+ "rc-tree": "~5.13.1",
+ "rc-tree-select": "~5.27.0",
+ "rc-upload": "~4.9.2",
+ "rc-util": "^5.44.4",
+ "scroll-into-view-if-needed": "^3.1.0",
+ "throttle-debounce": "^5.0.2"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ant-design"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
"node_modules/any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
@@ -6037,6 +6367,12 @@
"integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==",
"license": "MIT"
},
+ "node_modules/classnames": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
+ "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
+ "license": "MIT"
+ },
"node_modules/clean-css": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
@@ -6290,6 +6626,12 @@
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
+ "node_modules/compute-scroll-into-view": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz",
+ "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==",
+ "license": "MIT"
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -6353,6 +6695,15 @@
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
"license": "MIT"
},
+ "node_modules/copy-to-clipboard": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
+ "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
+ "license": "MIT",
+ "dependencies": {
+ "toggle-selection": "^1.0.6"
+ }
+ },
"node_modules/core-js": {
"version": "3.42.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.42.0.tgz",
@@ -6885,6 +7236,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/dayjs": {
+ "version": "1.11.13",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
+ "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
+ "license": "MIT"
+ },
"node_modules/debug": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
@@ -11427,6 +11784,15 @@
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"license": "MIT"
},
+ "node_modules/json2mq": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz",
+ "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==",
+ "license": "MIT",
+ "dependencies": {
+ "string-convert": "^0.2.0"
+ }
+ },
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -14242,6 +14608,618 @@
"node": ">=0.10.0"
}
},
+ "node_modules/rc-cascader": {
+ "version": "3.34.0",
+ "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.34.0.tgz",
+ "integrity": "sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.25.7",
+ "classnames": "^2.3.1",
+ "rc-select": "~14.16.2",
+ "rc-tree": "~5.13.0",
+ "rc-util": "^5.43.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-checkbox": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-3.5.0.tgz",
+ "integrity": "sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.25.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-collapse": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-3.9.0.tgz",
+ "integrity": "sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "2.x",
+ "rc-motion": "^2.3.4",
+ "rc-util": "^5.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-dialog": {
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.6.0.tgz",
+ "integrity": "sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/portal": "^1.0.0-8",
+ "classnames": "^2.2.6",
+ "rc-motion": "^2.3.0",
+ "rc-util": "^5.21.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-drawer": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-7.3.0.tgz",
+ "integrity": "sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.9",
+ "@rc-component/portal": "^1.1.1",
+ "classnames": "^2.2.6",
+ "rc-motion": "^2.6.1",
+ "rc-util": "^5.38.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-dropdown": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-4.2.1.tgz",
+ "integrity": "sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "^2.2.6",
+ "rc-util": "^5.44.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.11.0",
+ "react-dom": ">=16.11.0"
+ }
+ },
+ "node_modules/rc-field-form": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.7.0.tgz",
+ "integrity": "sha512-hgKsCay2taxzVnBPZl+1n4ZondsV78G++XVsMIJCAoioMjlMQR9YwAp7JZDIECzIu2Z66R+f4SFIRrO2DjDNAA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.0",
+ "@rc-component/async-validator": "^5.0.3",
+ "rc-util": "^5.32.2"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-image": {
+ "version": "7.12.0",
+ "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-7.12.0.tgz",
+ "integrity": "sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.2",
+ "@rc-component/portal": "^1.0.2",
+ "classnames": "^2.2.6",
+ "rc-dialog": "~9.6.0",
+ "rc-motion": "^2.6.2",
+ "rc-util": "^5.34.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-input": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/rc-input/-/rc-input-1.8.0.tgz",
+ "integrity": "sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "classnames": "^2.2.1",
+ "rc-util": "^5.18.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/rc-input-number": {
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.5.0.tgz",
+ "integrity": "sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/mini-decimal": "^1.0.1",
+ "classnames": "^2.2.5",
+ "rc-input": "~1.8.0",
+ "rc-util": "^5.40.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-mentions": {
+ "version": "2.20.0",
+ "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.20.0.tgz",
+ "integrity": "sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.22.5",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "^2.2.6",
+ "rc-input": "~1.8.0",
+ "rc-menu": "~9.16.0",
+ "rc-textarea": "~1.10.0",
+ "rc-util": "^5.34.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-menu": {
+ "version": "9.16.1",
+ "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.16.1.tgz",
+ "integrity": "sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "2.x",
+ "rc-motion": "^2.4.3",
+ "rc-overflow": "^1.3.1",
+ "rc-util": "^5.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-motion": {
+ "version": "2.9.5",
+ "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.5.tgz",
+ "integrity": "sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "classnames": "^2.2.1",
+ "rc-util": "^5.44.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-notification": {
+ "version": "5.6.4",
+ "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-5.6.4.tgz",
+ "integrity": "sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "2.x",
+ "rc-motion": "^2.9.0",
+ "rc-util": "^5.20.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-overflow": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.4.1.tgz",
+ "integrity": "sha512-3MoPQQPV1uKyOMVNd6SZfONi+f3st0r8PksexIdBTeIYbMX0Jr+k7pHEDvsXtR4BpCv90/Pv2MovVNhktKrwvw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "classnames": "^2.2.1",
+ "rc-resize-observer": "^1.0.0",
+ "rc-util": "^5.37.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-pagination": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-5.1.0.tgz",
+ "integrity": "sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.3.2",
+ "rc-util": "^5.38.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-picker": {
+ "version": "4.11.3",
+ "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-4.11.3.tgz",
+ "integrity": "sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.24.7",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "^2.2.1",
+ "rc-overflow": "^1.3.2",
+ "rc-resize-observer": "^1.4.0",
+ "rc-util": "^5.43.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "date-fns": ">= 2.x",
+ "dayjs": ">= 1.x",
+ "luxon": ">= 3.x",
+ "moment": ">= 2.x",
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ },
+ "peerDependenciesMeta": {
+ "date-fns": {
+ "optional": true
+ },
+ "dayjs": {
+ "optional": true
+ },
+ "luxon": {
+ "optional": true
+ },
+ "moment": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/rc-progress": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-4.0.0.tgz",
+ "integrity": "sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.2.6",
+ "rc-util": "^5.16.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-rate": {
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.13.1.tgz",
+ "integrity": "sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.2.5",
+ "rc-util": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-resize-observer": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz",
+ "integrity": "sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.20.7",
+ "classnames": "^2.2.1",
+ "rc-util": "^5.44.1",
+ "resize-observer-polyfill": "^1.5.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-segmented": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.7.0.tgz",
+ "integrity": "sha512-liijAjXz+KnTRVnxxXG2sYDGd6iLL7VpGGdR8gwoxAXy2KglviKCxLWZdjKYJzYzGSUwKDSTdYk8brj54Bn5BA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.1",
+ "classnames": "^2.2.1",
+ "rc-motion": "^2.4.4",
+ "rc-util": "^5.17.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.0.0",
+ "react-dom": ">=16.0.0"
+ }
+ },
+ "node_modules/rc-select": {
+ "version": "14.16.8",
+ "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.16.8.tgz",
+ "integrity": "sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/trigger": "^2.1.1",
+ "classnames": "2.x",
+ "rc-motion": "^2.0.1",
+ "rc-overflow": "^1.3.1",
+ "rc-util": "^5.16.1",
+ "rc-virtual-list": "^3.5.2"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/rc-slider": {
+ "version": "11.1.8",
+ "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.8.tgz",
+ "integrity": "sha512-2gg/72YFSpKP+Ja5AjC5DPL1YnV8DEITDQrcc1eASrUYjl0esptaBVJBh5nLTXCCp15eD8EuGjwezVGSHhs9tQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.2.5",
+ "rc-util": "^5.36.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-steps": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-6.0.1.tgz",
+ "integrity": "sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.16.7",
+ "classnames": "^2.2.3",
+ "rc-util": "^5.16.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-switch": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-4.1.0.tgz",
+ "integrity": "sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.21.0",
+ "classnames": "^2.2.1",
+ "rc-util": "^5.30.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-table": {
+ "version": "7.50.5",
+ "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.50.5.tgz",
+ "integrity": "sha512-FDZu8aolhSYd3v9KOc3lZOVAU77wmRRu44R0Wfb8Oj1dXRUsloFaXMSl6f7yuWZUxArJTli7k8TEOX2mvhDl4A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "@rc-component/context": "^1.4.0",
+ "classnames": "^2.2.5",
+ "rc-resize-observer": "^1.1.0",
+ "rc-util": "^5.44.3",
+ "rc-virtual-list": "^3.14.2"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-tabs": {
+ "version": "15.6.1",
+ "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.6.1.tgz",
+ "integrity": "sha512-/HzDV1VqOsUWyuC0c6AkxVYFjvx9+rFPKZ32ejxX0Uc7QCzcEjTA9/xMgv4HemPKwzBNX8KhGVbbumDjnj92aA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.2",
+ "classnames": "2.x",
+ "rc-dropdown": "~4.2.0",
+ "rc-menu": "~9.16.0",
+ "rc-motion": "^2.6.2",
+ "rc-resize-observer": "^1.0.0",
+ "rc-util": "^5.34.1"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-textarea": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.10.0.tgz",
+ "integrity": "sha512-ai9IkanNuyBS4x6sOL8qu/Ld40e6cEs6pgk93R+XLYg0mDSjNBGey6/ZpDs5+gNLD7urQ14po3V6Ck2dJLt9SA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.2.1",
+ "rc-input": "~1.8.0",
+ "rc-resize-observer": "^1.0.0",
+ "rc-util": "^5.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-tooltip": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-6.4.0.tgz",
+ "integrity": "sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.11.2",
+ "@rc-component/trigger": "^2.0.0",
+ "classnames": "^2.3.1",
+ "rc-util": "^5.44.3"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-tree": {
+ "version": "5.13.1",
+ "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-5.13.1.tgz",
+ "integrity": "sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "2.x",
+ "rc-motion": "^2.0.1",
+ "rc-util": "^5.16.1",
+ "rc-virtual-list": "^3.5.1"
+ },
+ "engines": {
+ "node": ">=10.x"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/rc-tree-select": {
+ "version": "5.27.0",
+ "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.27.0.tgz",
+ "integrity": "sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.25.7",
+ "classnames": "2.x",
+ "rc-select": "~14.16.2",
+ "rc-tree": "~5.13.0",
+ "rc-util": "^5.43.0"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/rc-upload": {
+ "version": "4.9.2",
+ "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.9.2.tgz",
+ "integrity": "sha512-nHx+9rbd1FKMiMRYsqQ3NkXUv7COHPBo3X1Obwq9SWS6/diF/A0aJ5OHubvwUAIDs+4RMleljV0pcrNUc823GQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "classnames": "^2.2.5",
+ "rc-util": "^5.2.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-util": {
+ "version": "5.44.4",
+ "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz",
+ "integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "react-is": "^18.2.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-util/node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+ "license": "MIT"
+ },
+ "node_modules/rc-virtual-list": {
+ "version": "3.18.6",
+ "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.18.6.tgz",
+ "integrity": "sha512-TQ5SsutL3McvWmmxqQtMIbfeoE3dGjJrRSfKekgby7WQMpPIFvv4ghytp5Z0s3D8Nik9i9YNOCqHBfk86AwgAA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.20.0",
+ "classnames": "^2.2.6",
+ "rc-resize-observer": "^1.0.0",
+ "rc-util": "^5.36.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
"node_modules/react": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
@@ -14756,6 +15734,12 @@
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
"license": "MIT"
},
+ "node_modules/resize-observer-polyfill": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+ "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
+ "license": "MIT"
+ },
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
@@ -15189,6 +16173,15 @@
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
+ "node_modules/scroll-into-view-if-needed": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz",
+ "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==",
+ "license": "MIT",
+ "dependencies": {
+ "compute-scroll-into-view": "^3.0.2"
+ }
+ },
"node_modules/select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -15818,6 +16811,12 @@
"safe-buffer": "~5.2.0"
}
},
+ "node_modules/string-convert": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz",
+ "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==",
+ "license": "MIT"
+ },
"node_modules/string-length": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
@@ -16613,6 +17612,15 @@
"integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==",
"license": "MIT"
},
+ "node_modules/throttle-debounce": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz",
+ "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.22"
+ }
+ },
"node_modules/thunky": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
@@ -16637,6 +17645,12 @@
"node": ">=8.0"
}
},
+ "node_modules/toggle-selection": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
+ "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==",
+ "license": "MIT"
+ },
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@@ -16885,7 +17899,7 @@
},
"node_modules/typescript": {
"version": "4.9.5",
- "resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.9.5.tgz",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"license": "Apache-2.0",
"peer": true,
diff --git a/front/package.json b/front/package.json
index 117259a..c25589f 100644
--- a/front/package.json
+++ b/front/package.json
@@ -11,6 +11,7 @@
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^13.5.0",
+ "antd": "^5.25.4",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.30.1",
diff --git a/front/src/AnimePage.js b/front/src/AnimePage.js
index d17f3cc..97bf453 100644
--- a/front/src/AnimePage.js
+++ b/front/src/AnimePage.js
@@ -30,17 +30,16 @@
];
const areaTabs = [
- { label: "国创", icon: <EmojiPeopleIcon fontSize="small" /> },
- { label: "日漫", icon: <EmailIcon fontSize="small" /> },
- { label: "欧美动漫", icon: <PersonIcon fontSize="small" /> },
- { label: "韩漫", icon: <EmojiPeopleIcon fontSize="small" /> },
- // { label: "其他", icon: <PersonIcon fontSize="small" /> },
+ { label: "大陆", icon: <EmojiPeopleIcon fontSize="small" /> },
+ { label: "港台", icon: <EmailIcon fontSize="small" /> },
+ { label: "欧美", icon: <PersonIcon fontSize="small" /> },
+ { label: "日韩", icon: <EmojiPeopleIcon fontSize="small" /> },
+ { label: "其他", icon: <PersonIcon fontSize="small" /> },
];
export default function AnimePage() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = React.useState(0);
- const [animeList, setAnimeList] = React.useState([]);
// 每个tab对应的动漫类型
const animeTypesList = [
@@ -52,15 +51,6 @@
];
const animeTypes = animeTypesList[activeTab] || [];
- React.useEffect(() => {
- // 假设后端接口为 /api/animes?area=大陆
- const area = areaTabs[activeTab].label;
- fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)
- .then(res => res.json())
- .then(data => setAnimeList(data))
- .catch(() => setAnimeList([]));
- }, [activeTab]);
-
return (
<div className="container">
{/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}
@@ -118,39 +108,21 @@
</tr>
</thead>
<tbody>
- {animeList.length > 0 ? (
- animeList.map((item, index) => (
- <tr key={item.id || index}>
- <td>
- <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {item.seedtag}
- </a>
- </td>
- <td>
- <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {item.title}
- </a>
- </td>
- <td>{item.user.username}</td>
- </tr>
- ))
- ) : (
- animeTypes.map((type, index) => (
- <tr key={type}>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {type}
- </a>
- </td>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- 种子{index + 1}
- </a>
- </td>
- <td>发布者{index + 1}</td>
- </tr>
- ))
- )}
+ {animeTypes.map((type, index) => (
+ <tr key={type}>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {type}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ 种子{index + 1}
+ </a>
+ </td>
+ <td>发布者{index + 1}</td>
+ </tr>
+ ))}
</tbody>
</table>
</div>
diff --git a/front/src/App.js b/front/src/App.js
index 240f958..225689e 100644
--- a/front/src/App.js
+++ b/front/src/App.js
@@ -1,5 +1,5 @@
import React from "react";
-import { BrowserRouter as Router, Routes, Route, useNavigate, Link } from "react-router-dom";
+import { BrowserRouter as Router, Routes, Route, useNavigate, Link, Navigate } from "react-router-dom";
import MovieIcon from "@mui/icons-material/Movie";
import EmailIcon from "@mui/icons-material/Email";
import MusicNoteIcon from "@mui/icons-material/MusicNote";
@@ -19,6 +19,8 @@
import UserProfile from "./UserProfile";
import PublishPage from "./PublishPage";
import TorrentDetailPage from './TorrentDetailPage';
+import LoginPage from './LoginPage';
+import RegisterPage from './RegisterPage';
const navItems = [
{ label: "电影", icon: <MovieIcon />, path: "/movie" },
@@ -142,7 +144,9 @@
return (
<Router>
<Routes>
- <Route path="/" element={<Home />} />
+ <Route path="/login" element={<LoginPage />} />
+ <Route path="/register" element={<RegisterPage />} />
+ <Route path="/" element={<Navigate to="/login" replace />} />
<Route path="/movie" element={<MoviePage />} />
<Route path="/tv" element={<TVPage />} />
<Route path="/music" element={<MusicPage />} />
diff --git a/front/src/GamePage.js b/front/src/GamePage.js
index 2528acd..23383b6 100644
--- a/front/src/GamePage.js
+++ b/front/src/GamePage.js
@@ -21,13 +21,7 @@
{ label: "发布", icon: <AccountCircleIcon />, path: "/publish" }, // Added Publish option
];
-const gameTypesList = [
- ["PC"],
- ["主机"],
- ["移动"],
- ["掌机"],
- ["视频"]
-];
+const gameTypes = ["PC", "主机", "移动", "掌机", "视频"];
const areaTabs = [
{ label: "PC", icon: <MovieIcon fontSize="small" /> },
@@ -37,20 +31,15 @@
{ label: "视频", icon: <PersonIcon fontSize="small" /> },
];
+const exampleTorrents = [
+ { type: "RPG", title: "实例1", id: 1 },
+ { type: "Shooter", title: "实例2", id: 2 },
+ { type: "Adventure", title: "实例3", id: 3 },
+];
+
export default function GamePage() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = useState(0);
- const [gameList, setGameList] = useState([]);
-
- const gameTypes = gameTypesList[activeTab] || [];
-
- React.useEffect(() => {
- const area = areaTabs[activeTab].label;
- fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)
- .then(res => res.json())
- .then(data => setGameList(data))
- .catch(() => setGameList([]));
- }, [activeTab]);
return (
<div className="container">
@@ -164,39 +153,27 @@
</tr>
</thead>
<tbody>
- {gameList.length > 0 ? (
- gameList.map((item, idx) => (
- <tr key={item.id || idx}>
- <td>
- <a href={`/torrent/${item.seedid}`} style={{ color: "#1a237e", textDecoration: "none" }}>
- {item.seedtag}
- </a>
- </td>
- <td>
- <a href={`/torrent/${item.seedid}`} style={{ color: "#1a237e", textDecoration: "none" }}>
- {item.title}
- </a>
- </td>
- <td>{item.user.username}</td>
- </tr>
- ))
- ) : (
- gameTypes.map((type, idx) => (
- <tr key={type}>
- <td>
- <a href={`/torrent/${type}`} style={{ color: "#1a237e", textDecoration: "none" }}>
- {type}
- </a>
- </td>
- <td>
- <a href={`/torrent/${type}`} style={{ color: "#1a237e", textDecoration: "none" }}>
- 种子{idx + 1}
- </a>
- </td>
- <td></td>
- </tr>
- ))
- )}
+ {gameTypes.map((type, idx) => (
+ <tr key={type}>
+ <td>
+ <a
+ href={`/torrent/${type}`}
+ style={{ color: "#1a237e", textDecoration: "none" }}
+ >
+ {type}
+ </a>
+ </td>
+ <td>
+ <a
+ href={`/torrent/${type}`}
+ style={{ color: "#1a237e", textDecoration: "none" }}
+ >
+ 种子{idx + 1}
+ </a>
+ </td>
+ <td></td>
+ </tr>
+ ))}
</tbody>
</table>
</div>
diff --git a/front/src/InfoPage.js b/front/src/InfoPage.js
index 755d922..1e00527 100644
--- a/front/src/InfoPage.js
+++ b/front/src/InfoPage.js
@@ -40,7 +40,6 @@
export default function InfoPage() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = React.useState(0);
- const [infoList, setInfoList] = React.useState(0);
// 每个tab对应的资料类型
const infoTypesList = [
@@ -52,18 +51,6 @@
];
const infoTypes = infoTypesList[activeTab] || [];
- React.useEffect(() => {
- // 这里假设后端接口为 /api/get-seed-list-by-tag?tag=大陆
- const area = areaTabs[activeTab].label;
- fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)
- .then(res => res.json())
- .then(data => {
- console.log('资料区返回数据:', data);
- setInfoList(data);
- })
- .catch(() => setInfoList([]));
- }, [activeTab]);
-
return (
<div className="container">
{/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}
@@ -176,39 +163,27 @@
</tr>
</thead>
<tbody>
- {infoList.length > 0 ? (
- infoList.map((item, index) => (
- <tr key={item.id || index}>
- <td>
- <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {item.seedtag}
- </a>
- </td>
- <td>
- <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {item.title}
- </a>
- </td>
- <td>{item.user.username}</td>
- </tr>
- ))
- ) : (
- infoTypesList.map((type, index) => (
- <tr key={type}>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {type}
- </a>
- </td>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- 种子{index + 1}
- </a>
- </td>
- <td>发布者{index + 1}</td>
- </tr>
- ))
- )}
+ {infoTypes.map((type, index) => (
+ <tr key={type}>
+ <td>
+ <a
+ href={`/torrent/${type}`}
+ style={{ color: "#1a237e", textDecoration: "none" }}
+ >
+ {type}
+ </a>
+ </td>
+ <td>
+ <a
+ href={`/torrent/${type}`}
+ style={{ color: "#1a237e", textDecoration: "none" }}
+ >
+ 种子{index + 1}
+ </a>
+ </td>
+ <td>发布者{index + 1}</td>
+ </tr>
+ ))}
</tbody>
</table>
</div>
diff --git a/front/src/LoginPage.js b/front/src/LoginPage.js
new file mode 100644
index 0000000..59a1b2b
--- /dev/null
+++ b/front/src/LoginPage.js
@@ -0,0 +1,100 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { Input } from 'antd';
+import './App.css';
+
+const LoginPage = () => {
+ const [formData, setFormData] = useState({ username: '', password: '' });
+ const [errorMessage, setErrorMessage] = useState('');
+ const navigate = useNavigate();
+
+ const handleChange = (e) => {
+ const { name, value } = e.target;
+ setFormData({ ...formData, [name]: value });
+ };
+
+ const handleLogin = () => {
+ if (formData.password.length < 8) {
+ setErrorMessage('密码必须至少包含八位字符!');
+ return;
+ }
+
+ // Simulate successful login
+ setErrorMessage('');
+ navigate('/');
+ };
+
+ const handleRegister = () => {
+ navigate('/register');
+ };
+
+ // 自动填充注册信息
+ React.useEffect(() => {
+ const regUser = sessionStorage.getItem('registeredUser');
+ if (regUser) {
+ try {
+ const { username, password } = JSON.parse(regUser);
+ setFormData({ username, password });
+ sessionStorage.removeItem('registeredUser');
+ } catch {}
+ }
+ }, []);
+
+ return (
+ <div className="login-page" style={{ minHeight: '100vh', background: 'linear-gradient(135deg, #e0e7ff 0%, #f0f8ff 100%)', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
+ <div className="login-form-container" style={{ width: 360, padding: '40px 32px 80px 32px', borderRadius: 18, boxShadow: '0 8px 32px rgba(60, 80, 180, 0.10)', background: '#fff', position: 'relative' }}>
+ <h1 style={{ textAlign: 'center', marginBottom: 32, color: '#222', fontWeight: 700, fontSize: 32, letterSpacing: 2 }}>欢迎登录</h1>
+ <form className="login-form">
+ <div className="form-row" style={{ marginBottom: 24 }}>
+ <label htmlFor="username" style={{ display: 'block', marginBottom: 8, color: '#333', fontWeight: 500, fontSize: 16 }}>用户名</label>
+ <Input
+ placeholder="请输入用户名"
+ id="username"
+ name="username"
+ value={formData.username}
+ onChange={handleChange}
+ required
+ style={{ width: '100%', padding: '12px', borderRadius: 8, fontSize: 16, background: '#f7faff' }}
+ />
+ </div>
+ <div className="form-row" style={{ marginBottom: 24 }}>
+ <label htmlFor="password" style={{ display: 'block', marginBottom: 8, color: '#333', fontWeight: 500, fontSize: 16 }}>密码</label>
+ <div style={{ display: 'flex', alignItems: 'center' }}>
+ <Input
+ placeholder="请输入密码"
+ type="password"
+ id="password"
+ name="password"
+ value={formData.password}
+ onChange={handleChange}
+ required
+ style={{ flex: 1, padding: '12px', borderRadius: 8, fontSize: 16, background: '#f7faff' }}
+ />
+ </div>
+ </div>
+ {errorMessage && <p style={{ color: '#e53935', textAlign: 'center', marginBottom: 18, fontWeight: 500 }}>{errorMessage}</p>}
+ <div className="form-row button-row" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginTop: 32, position: 'static', gap: 16 }}>
+ <button
+ type="button"
+ className="login-button"
+ onClick={handleLogin}
+ style={{ width: 120, padding: '10px 0', borderRadius: 8, border: 'none', background: 'linear-gradient(90deg, #34c759 0%, #5be584 100%)', color: '#fff', fontWeight: 600, fontSize: 16, cursor: 'pointer', boxShadow: '0 2px 8px #b2eac2', marginBottom: 12 }}
+ >
+ 登录
+ </button>
+ <button
+ type="button"
+ className="register-button"
+ onClick={handleRegister}
+ style={{ width: 120, padding: '10px 0', borderRadius: 8, border: 'none', background: 'linear-gradient(90deg, #4f8cff 0%, #6ad1ff 100%)', color: '#fff', fontWeight: 600, fontSize: 16, cursor: 'pointer', boxShadow: '0 2px 8px #b2d8ea' }}
+ >
+ 注册
+ </button>
+ </div>
+ </form>
+ </div>
+ </div>
+ );
+};
+
+export default LoginPage;
diff --git a/front/src/MoviePage.js b/front/src/MoviePage.js
index c84797b..20e4a4f 100644
--- a/front/src/MoviePage.js
+++ b/front/src/MoviePage.js
@@ -26,7 +26,7 @@
{ label: "港台", icon: <EmailIcon fontSize="small" /> },
{ label: "欧美", icon: <PersonIcon fontSize="small" /> },
{ label: "日韩", icon: <EmojiPeopleIcon fontSize="small" /> },
- // { label: "其他", icon: <PersonIcon fontSize="small" /> },
+ { label: "其他", icon: <PersonIcon fontSize="small" /> },
];
const exampleTorrents = [
@@ -38,7 +38,6 @@
export default function MoviePage() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = React.useState(0);
- const [movieList, setMovieList] = React.useState([]);
// 每个tab对应的电影类型
const movieTypesList = [
@@ -50,18 +49,6 @@
];
const movieTypes = movieTypesList[activeTab] || [];
- React.useEffect(() => {
- // 这里假设后端接口为 /api/get-seed-list-by-tag?tag=大陆
- const area = areaTabs[activeTab].label;
- fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)
- .then(res => res.json())
- .then(data => {
- console.log('电影区返回数据:', data);
- setMovieList(data);
- })
- .catch(() => setMovieList([]));
- }, [activeTab]);
-
return (
<div className="container">
{/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}
@@ -119,39 +106,21 @@
</tr>
</thead>
<tbody>
- {movieList.length > 0 ? (
- movieList.map((item, index) => (
- <tr key={item.id || index}>
- <td>
- <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {item.seedtag}
- </a>
- </td>
- <td>
- <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {item.title}
- </a>
- </td>
- <td>{item.user.username}</td>
- </tr>
- ))
- ) : (
- movieTypes.map((type, index) => (
- <tr key={type}>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {type}
- </a>
- </td>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- 种子{index + 1}
- </a>
- </td>
- <td>发布者{index + 1}</td>
- </tr>
- ))
- )}
+ {movieTypes.map((type, index) => (
+ <tr key={type}>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {type}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ 种子{index + 1}
+ </a>
+ </td>
+ <td>发布者{index + 1}</td>
+ </tr>
+ ))}
</tbody>
</table>
</div>
diff --git a/front/src/MusicPage.js b/front/src/MusicPage.js
index b39b0ee..4a9c465 100644
--- a/front/src/MusicPage.js
+++ b/front/src/MusicPage.js
@@ -22,11 +22,11 @@
];
const areaTabs = [
- { label: "古典音乐", icon: <MovieIcon fontSize="small" /> },
- { label: "流行音乐", icon: <EmailIcon fontSize="small" /> },
- { label: "摇滚", icon: <PersonIcon fontSize="small" /> },
- { label: "电子音乐", icon: <EmojiPeopleIcon fontSize="small" /> },
- { label: "说唱", icon: <PersonIcon fontSize="small" /> },
+ { label: "大陆", icon: <MovieIcon fontSize="small" /> },
+ { label: "港台", icon: <EmailIcon fontSize="small" /> },
+ { label: "欧美", icon: <PersonIcon fontSize="small" /> },
+ { label: "日韩", icon: <EmojiPeopleIcon fontSize="small" /> },
+ { label: "其他", icon: <PersonIcon fontSize="small" /> },
];
const exampleTorrents = [
@@ -38,7 +38,6 @@
export default function MusicPage() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = React.useState(0);
- const [musicList, setMusicList] = React.useState([]);
// 每个tab对应的音乐类型
const musicTypesList = [
@@ -50,15 +49,6 @@
];
const musicTypes = musicTypesList[activeTab] || [];
- React.useEffect(() => {
- // 假设后端接口为 /api/musics?area=大陆
- const area = areaTabs[activeTab].label;
- fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)
- .then(res => res.json())
- .then(data => setMusicList(data))
- .catch(() => setMusicList([]));
- }, [activeTab]);
-
return (
<div className="container music-bg">
{/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}
@@ -116,39 +106,21 @@
</tr>
</thead>
<tbody>
- {musicList.length > 0 ? (
- musicList.map((item, index) => (
- <tr key={item.id || index}>
- <td>
- <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {item.seedtag}
- </a>
- </td>
- <td>
- <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {item.title}
- </a>
- </td>
- <td>{item.user.username}</td>
- </tr>
- ))
- ) : (
- musicTypes.map((type, index) => (
- <tr key={type}>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {type}
- </a>
- </td>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- 种子{index + 1}
- </a>
- </td>
- <td>发布者{index + 1}</td>
- </tr>
- ))
- )}
+ {musicTypes.map((type, index) => (
+ <tr key={type}>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {type}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ 种子{index + 1}
+ </a>
+ </td>
+ <td>发布者{index + 1}</td>
+ </tr>
+ ))}
</tbody>
</table>
</div>
diff --git a/front/src/PublishPage.js b/front/src/PublishPage.js
index a1a0e8f..81ac790 100644
--- a/front/src/PublishPage.js
+++ b/front/src/PublishPage.js
@@ -9,17 +9,6 @@
title: '',
subtitle: ''
});
- const [subType, setSubType] = useState('');
-
- const typeOptions = {
- '电影': ['大陆', '港台', '欧美', '日韩'],
- '剧集': ['国产电视剧', '港剧', '欧美剧', '日韩剧'],
- '音乐': ['古典音乐', '流行音乐', '摇滚', '电子音乐', '说唱'],
- '动漫': ['国创', '日漫', '欧美动漫', '韩漫'],
- '游戏': ['PC', '主机', '移动', '掌机', '视频'],
- '体育': ['篮球', '足球', '羽毛球', '排球', '电竞'],
- '资料': ['出版物', '学习教程', '素材模板', '演讲交流', '日常娱乐'],
- };
const handleChange = (e) => {
const { name, value } = e.target;
@@ -36,37 +25,9 @@
}
};
- const handleSubmit = async (e) => {
+ const handleSubmit = (e) => {
e.preventDefault();
- // 假设userid和tag可以从表单或用户信息中获取,这里用示例数据
- const userid = '550e8400-e29b-41d4-a716-446655440000';
- const tag = formData.type ? formData.type : '高清';
- if (!formData.torrentFile) {
- alert('请上传.torrent文件');
- return;
- }
- const data = new FormData();
- data.append('userid', userid);
- data.append('title', formData.title);
- data.append('tag', subType);
- data.append('file', formData.torrentFile);
- data.append('subtitle', formData.subtitle);
- // data.append('subtype', subType);
- // console.log(data.get('tag'));
-
- try {
- const response = await fetch('http://192.168.5.9:8080/api/save-torrent', {
- method: 'POST',
- body: data,
- });
- if (response.ok) {
- alert('上传成功!');
- } else {
- alert('上传失败');
- }
- } catch (err) {
- alert('网络错误');
- }
+ console.log('Form Data Submitted:', formData);
};
return (
@@ -75,7 +36,7 @@
<form onSubmit={handleSubmit} className="publish-form">
<div className="form-row">
<label htmlFor="type">类型</label>
- <select name="type" id="type" value={formData.type} onChange={e => { handleChange(e); setSubType(''); }} required>
+ <select name="type" id="type" value={formData.type} onChange={handleChange} required>
<option value="">请选择类型</option>
<option value="电影">电影</option>
<option value="剧集">剧集</option>
@@ -86,17 +47,6 @@
<option value="资料">资料</option>
</select>
</div>
- {formData.type && typeOptions[formData.type] && (
- <div className="form-row">
- <label htmlFor="subtype">具体类型</label>
- <select name="subtype" id="subtype" value={subType} onChange={e => setSubType(e.target.value)} required>
- <option value="">请选择具体类型</option>
- {typeOptions[formData.type].map(opt => (
- <option key={opt} value={opt}>{opt}</option>
- ))}
- </select>
- </div>
- )}
<div className="form-row">
<label htmlFor="torrentFile">种子文件</label>
@@ -123,7 +73,7 @@
</div>
<div className="form-row">
- <label htmlFor="subtitle">副标题</label>
+ <label htmlFor="subtitle">种子简介</label>
<input
type="text"
id="subtitle"
diff --git a/front/src/RegisterPage.js b/front/src/RegisterPage.js
new file mode 100644
index 0000000..ffd11a4
--- /dev/null
+++ b/front/src/RegisterPage.js
@@ -0,0 +1,109 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { Input } from 'antd';
+import './App.css';
+
+const RegisterPage = () => {
+ const [formData, setFormData] = useState({ username: '', password: '', inviteCode: '', confirmPassword: '' });
+ const [errorMessage, setErrorMessage] = useState('');
+ const navigate = useNavigate();
+
+ const handleChange = (e) => {
+ const { name, value } = e.target;
+ setFormData({ ...formData, [name]: value });
+ };
+
+ const handleRegister = () => {
+ if (formData.password.length !== 8) {
+ setErrorMessage('密码必须为八位字符!');
+ return;
+ }
+
+ if (formData.password !== formData.confirmPassword) {
+ setErrorMessage('两次输入的密码不匹配!');
+ return;
+ }
+
+ // Simulate successful registration
+ setErrorMessage('');
+ // 存储注册信息到 sessionStorage
+ sessionStorage.setItem('registeredUser', JSON.stringify({ username: formData.username, password: formData.password }));
+ alert('注册成功!');
+ navigate('/login');
+ };
+
+ return (
+ <div className="register-page" style={{ minHeight: '100vh', background: 'linear-gradient(135deg, #e0e7ff 0%, #f0f8ff 100%)', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
+ <div className="register-form-container" style={{ width: 360, padding: '40px 32px 64px 32px', borderRadius: 18, boxShadow: '0 8px 32px rgba(60, 80, 180, 0.10)', background: '#fff', position: 'relative' }}>
+ <h1 style={{ textAlign: 'center', marginBottom: 32, color: '#222', fontWeight: 700, fontSize: 32, letterSpacing: 2 }}>欢迎注册</h1>
+ <form className="register-form">
+ <div className="form-row" style={{ marginBottom: 24 }}>
+ <label htmlFor="inviteCode" style={{ display: 'block', marginBottom: 8, color: '#333', fontWeight: 500, fontSize: 16 }}>被邀请邮箱</label>
+ <Input
+ placeholder="请输入被邀请邮箱"
+ id="inviteCode"
+ name="inviteCode"
+ value={formData.inviteCode || ''}
+ onChange={handleChange}
+ required
+ style={{ width: '100%', padding: '12px', borderRadius: 8, fontSize: 16, background: '#f7faff' }}
+ />
+ </div>
+ <div className="form-row" style={{ marginBottom: 24 }}>
+ <label htmlFor="username" style={{ display: 'block', marginBottom: 8, color: '#333', fontWeight: 500, fontSize: 16 }}>用户名</label>
+ <Input
+ placeholder="请输入用户名"
+ id="username"
+ name="username"
+ value={formData.username}
+ onChange={handleChange}
+ required
+ style={{ width: '100%', padding: '12px', borderRadius: 8, fontSize: 16, background: '#f7faff' }}
+ />
+ </div>
+ <div className="form-row" style={{ marginBottom: 24 }}>
+ <label htmlFor="password" style={{ display: 'block', marginBottom: 8, color: '#333', fontWeight: 500, fontSize: 16 }}>密码</label>
+ <div style={{ display: 'flex', alignItems: 'center' }}>
+ <Input
+ placeholder="请输入密码"
+ type="password"
+ id="password"
+ name="password"
+ value={formData.password}
+ onChange={handleChange}
+ required
+ style={{ flex: 1, padding: '12px', borderRadius: 8, fontSize: 16, background: '#f7faff' }}
+ />
+ </div>
+ </div>
+ <div className="form-row" style={{ marginBottom: 24 }}>
+ <label htmlFor="confirmPassword" style={{ display: 'block', marginBottom: 8, color: '#333', fontWeight: 500, fontSize: 16 }}>确认密码</label>
+ <Input
+ placeholder="请再次输入密码"
+ type="password"
+ id="confirmPassword"
+ name="confirmPassword"
+ value={formData.confirmPassword || ''}
+ onChange={handleChange}
+ required
+ style={{ width: '100%', padding: '12px', borderRadius: 8, fontSize: 16, background: '#f7faff' }}
+ />
+ </div>
+ {errorMessage && <p style={{ color: '#e53935', textAlign: 'center', marginBottom: 18, fontWeight: 500 }}>{errorMessage}</p>}
+ <div className="form-row button-row" style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', position: 'absolute', left: 0, right: 0, bottom: 24 }}>
+ <button
+ type="button"
+ className="register-button"
+ onClick={handleRegister}
+ style={{ width: 120, padding: '10px 0', borderRadius: 8, border: 'none', background: 'linear-gradient(90deg, #4f8cff 0%, #6ad1ff 100%)', color: '#fff', fontWeight: 600, fontSize: 16, cursor: 'pointer', boxShadow: '0 2px 8px #b2d8ea' }}
+ >
+ 注册
+ </button>
+ </div>
+ </form>
+ </div>
+ </div>
+ );
+};
+
+export default RegisterPage;
diff --git a/front/src/SportPage.js b/front/src/SportPage.js
index 3299a93..569f022 100644
--- a/front/src/SportPage.js
+++ b/front/src/SportPage.js
@@ -40,7 +40,6 @@
export default function SportPage() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = React.useState(0);
- const [sportList, setSportList] = React.useState([]);
// 每个tab对应的运动类型
const sportTypesList = [
@@ -52,15 +51,6 @@
];
const sportTypes = sportTypesList[activeTab] || [];
- React.useEffect(() => {
- // 假设后端接口为 /api/sports?area=篮球
- const area = areaTabs[activeTab].label;
- fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)
- .then(res => res.json())
- .then(data => setSportList(data))
- .catch(() => setSportList([]));
- }, [activeTab]);
-
return (
<div className="container">
{/* 顶部空白与音乐界面一致,用户栏绝对定位在页面右上角 */}
@@ -173,39 +163,27 @@
</tr>
</thead>
<tbody>
- {sportList.length > 0 ? (
- sportList.map((item, index) => (
- <tr key={item.id || index}>
- <td>
- <a href={`/torrent/${item.seedid}`} style={{ color: "#1a237e", textDecoration: "none" }}>
- {item.seedtag}
- </a>
- </td>
- <td>
- <a href={`/torrent/${item.seedid}`} style={{ color: "#1a237e", textDecoration: "none" }}>
- {item.title}
- </a>
- </td>
- <td>{item.user.username}</td>
- </tr>
- ))
- ) : (
- sportTypes.map((type, index) => (
- <tr key={type}>
- <td>
- <a href={`/torrent/${type}`} style={{ color: "#1a237e", textDecoration: "none" }}>
- {type}
- </a>
- </td>
- <td>
- <a href={`/torrent/${type}`} style={{ color: "#1a237e", textDecoration: "none" }}>
- 种子{index + 1}
- </a>
- </td>
- <td>发布者{index + 1}</td>
- </tr>
- ))
- )}
+ {sportTypes.map((type, index) => (
+ <tr key={type}>
+ <td>
+ <a
+ href={`/torrent/${type}`}
+ style={{ color: "#1a237e", textDecoration: "none" }}
+ >
+ {type}
+ </a>
+ </td>
+ <td>
+ <a
+ href={`/torrent/${type}`}
+ style={{ color: "#1a237e", textDecoration: "none" }}
+ >
+ 种子{index + 1}
+ </a>
+ </td>
+ <td>发布者{index + 1}</td>
+ </tr>
+ ))}
</tbody>
</table>
</div>
diff --git a/front/src/TVPage.js b/front/src/TVPage.js
index bf1db6c..13b7391 100644
--- a/front/src/TVPage.js
+++ b/front/src/TVPage.js
@@ -21,37 +21,40 @@
{ label: "发布", icon: <AccountCircleIcon />, path: "/publish" }, // Added Publish option
];
-const tvTypesList = [
- ["华语剧集(大陆)", "欧美剧集", "日韩剧集", "港台剧集", "其他"], // 大陆
- ["港台都市", "港台爱情", "港台悬疑", "港台其他"], // 港台
- ["欧美悬疑", "欧美历史", "欧美其他"], // 欧美
- ["日韩青春", "日韩家庭", "日韩其他"], // 日韩
- ["其他类型1", "其他类型2"] // 其他
+const tvTypes = [
+ "华语剧集(大陆)",
+ "欧美剧集",
+ "日韩剧集",
+ "港台剧集",
+ "其他"
];
const areaTabs = [
- { label: "国产电视剧", icon: <MovieIcon fontSize="small" /> },
- { label: "港剧", icon: <EmailIcon fontSize="small" /> },
- { label: "欧美剧", icon: <PersonIcon fontSize="small" /> },
- { label: "日韩剧", icon: <EmojiPeopleIcon fontSize="small" /> },
- // { label: "其他", icon: <PersonIcon fontSize="small" /> },
+ { label: "大陆", icon: <MovieIcon fontSize="small" /> },
+ { label: "港台", icon: <EmailIcon fontSize="small" /> },
+ { label: "欧美", icon: <PersonIcon fontSize="small" /> },
+ { label: "日韩", icon: <EmojiPeopleIcon fontSize="small" /> },
+ { label: "其他", icon: <PersonIcon fontSize="small" /> },
+];
+
+const exampleTorrents = [
+ { type: "Drama", title: "实例1", id: 1 },
+ { type: "Comedy", title: "实例2", id: 2 },
+ { type: "Sci-Fi", title: "实例3", id: 3 },
];
export default function TVPage() {
const navigate = useNavigate();
const [activeTab, setActiveTab] = React.useState(0);
- const [tvList, setTvList] = React.useState([]);
-
- React.useEffect(() => {
- // 假设后端接口为 /api/tvs?area=大陆
- const area = areaTabs[activeTab].label;
- fetch(`http://192.168.5.9:8080/api/get-seed-list-by-tag?tag=${encodeURIComponent(area)}`)
- .then(res => res.json())
- .then(data => setTvList(data))
- .catch(() => setTvList([]));
- }, [activeTab]);
// 每个tab对应的剧集类型
+ const tvTypesList = [
+ ["华语剧集(大陆)", "欧美剧集", "日韩剧集", "港台剧集", "其他"], // 大陆
+ ["港台都市", "港台爱情", "港台悬疑", "港台其他"], // 港台
+ ["欧美悬疑", "欧美历史", "欧美其他"], // 欧美
+ ["日韩青春", "日韩家庭", "日韩其他"], // 日韩
+ ["其他类型1", "其他类型2"] // 其他
+ ];
const tvTypes = tvTypesList[activeTab] || [];
return (
@@ -111,39 +114,21 @@
</tr>
</thead>
<tbody>
- {tvList.length > 0 ? (
- tvList.map((item, index) => (
- <tr key={item.id || index}>
- <td>
- <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {item.seedtag}
- </a>
- </td>
- <td>
- <a href={`/torrent/${item.seedid}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {item.title}
- </a>
- </td>
- <td>{item.user.username}</td>
- </tr>
- ))
- ) : (
- tvTypes.map((type, index) => (
- <tr key={type}>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- {type}
- </a>
- </td>
- <td>
- <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
- 种子{index + 1}
- </a>
- </td>
- <td>发布者{index + 1}</td>
- </tr>
- ))
- )}
+ {tvTypes.map((type, index) => (
+ <tr key={type}>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ {type}
+ </a>
+ </td>
+ <td>
+ <a href={`/torrent/${type}`} style={{ color: '#1a237e', textDecoration: 'none' }}>
+ 种子{index + 1}
+ </a>
+ </td>
+ <td></td>
+ </tr>
+ ))}
</tbody>
</table>
</div>
diff --git a/front/src/TorrentDetailPage.js b/front/src/TorrentDetailPage.js
index 8d3ee43..a23dce7 100644
--- a/front/src/TorrentDetailPage.js
+++ b/front/src/TorrentDetailPage.js
@@ -4,81 +4,14 @@
export default function TorrentDetailPage() {
const { torrentId } = useParams();
- const [detail, setDetail] = React.useState(null);
- const [loading, setLoading] = React.useState(true);
- const [error, setError] = React.useState(null);
- // 假设你从某个地方获取了 userId(例如登录状态、localStorage 等)
- const [userId] = React.useState('user1550e8400-e29b-41d4-a716-44665544000023'); // 替换为实际的用户 ID
-
- const handleClick = () => {
- // 构造下载 URL,包含 userId 和 torrentId 参数
- console.log(torrentId)
- const downloadUrl = `http://192.168.5.9:8080/api/get-torrent?userId=${encodeURIComponent(userId)}&torrentId=${encodeURIComponent(torrentId)}`;
-
- // 发起 GET 请求下载文件
- fetch(downloadUrl)
- .then(response => {
- if (!response.ok) {
- throw new Error('下载失败');
- }
- return response.blob();
- })
- .then(blob => {
- // 创建下载链接并触发下载
- const url = window.URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = `torrent-${torrentId}.torrent`;
- document.body.appendChild(a);
- a.click();
- window.URL.revokeObjectURL(url);
- document.body.removeChild(a);
- })
- .catch(error => {
- console.error('下载错误:', error);
- alert('下载失败: ' + error.message);
- });
- };
-
- React.useEffect(() => {
- setLoading(true);
- setError(null);
- fetch(`http://192.168.5.9:8080/api/torrent-detail?id=${encodeURIComponent(torrentId)}`)
- .then(res => {
- if (!res.ok) throw new Error('网络错误');
- return res.json();
- })
- .then(data => {
- setDetail(data);
- setLoading(false);
- })
- .catch(err => {
- setError(err.message);
- setLoading(false);
- });
- }, [torrentId]);
-
- if (loading) return <div className="container"><h1>加载中...</h1></div>;
- if (error) return <div className="container"><h1>加载失败: {error}</h1></div>;
- if (!detail) return <div className="container"><h1>未找到详情</h1></div>;
return (
<div className="container">
<h1>种子详情页</h1>
- <h2 style={{ fontSize: 'inherit', fontWeight: 'normal', textAlign: 'left' }}>标题: {detail.title || `种子${torrentId}`}</h2>
- <p style={{ fontSize: 'inherit', textAlign: 'left' }}>简介: {detail.description || `这是种子${torrentId}的详细信息。`}</p>
+ <h2 style={{ fontSize: 'inherit', fontWeight: 'normal', textAlign: 'left' }}>标题: 种子{torrentId}</h2>
+ <p style={{ fontSize: 'inherit', textAlign: 'left' }}>简介: 这是种子{torrentId}的详细信息。</p>
<div style={{ textAlign: 'center', marginTop: '20px' }}>
- <button
- style={{
- padding: '10px 20px',
- fontSize: '16px',
- cursor: 'pointer',
- backgroundColor: '#d3f0ff',
- border: 'none',
- borderRadius: '4px'
- }}
- onClick={handleClick}
- >
+ <button style={{ padding: '10px 20px', fontSize: '16px', cursor: 'pointer', backgroundColor: '#d3f0ff', border: 'none', borderRadius: '4px' }}>
下载
</button>
</div>
diff --git a/front/src/UserProfile.js b/front/src/UserProfile.js
index 4737033..0269723 100644
--- a/front/src/UserProfile.js
+++ b/front/src/UserProfile.js
@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from "react";
+import React, { useState } from "react";
import AccountCircleIcon from "@mui/icons-material/AccountCircle";
import { useNavigate } from "react-router-dom";
import "./App.css";
@@ -6,109 +6,21 @@
export default function UserProfile() {
const navigate = useNavigate();
const [userInfo, setUserInfo] = useState({
- avatar_url: "",
username: "示例用户",
email: "user@example.com",
- invite_left: "",
+ company: "",
school: "",
- account_status: "",
- gender: "",
+ birthday: "",
});
const [tempUserInfo, setTempUserInfo] = useState({ ...userInfo });
- const [userSeeds, setUserSeeds] = useState([]);
- const [userStats, setUserStats] = useState({
- magic: 0,
- upload: 0,
- download: 0,
- ratio: 0,
- });
-
- // 新增:根据userid从后端获取用户信息
- useEffect(() => {
- const fetchUserInfo = async () => {
- // 假设userid存储在localStorage或其他地方
- // const userid = localStorage.getItem("userid");
- const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
- if (!userid) return;
- try {
- const res = await fetch(`/api/user-profile?userid=${userid}`);
- if (res.ok) {
- const data = await res.json();
- setUserInfo(data);
- setTempUserInfo(data);
- }
- } catch (err) {
- // 可以根据需要处理错误
- console.error("获取用户信息失败", err);
- }
- };
- fetchUserInfo();
- }, []);
-
- // 动态加载用户上传种子列表
- useEffect(() => {
- const fetchUserSeeds = async () => {
- // const userid = localStorage.getItem("userid");
- const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
- if (!userid) return;
- try {
- const res = await fetch(`/api/user-seeds?userid=${userid}`);
- if (res.ok) {
- const data = await res.json();
- setUserSeeds(data);
- }
- } catch (err) {
- console.error("获取种子列表失败", err);
- }
- };
- fetchUserSeeds();
- }, []);
-
- // 动态加载用户活跃度信息
- useEffect(() => {
- const fetchUserStats = async () => {
- // const userid = localStorage.getItem("userid");
- const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
- if (!userid) return;
- try {
- const res = await fetch(`/api/user-stats?userid=${userid}`);
- if (res.ok) {
- const data = await res.json();
- setUserStats(data);
- }
- } catch (err) {
- console.error("获取活跃度信息失败", err);
- }
- };
- fetchUserStats();
- }, []);
const handleInputChange = (field, value) => {
setTempUserInfo({ ...tempUserInfo, [field]: value });
};
- const handleSave = async () => {
+ const handleSave = () => {
setUserInfo({ ...tempUserInfo });
- // 获取userid
- // const userid = localStorage.getItem("userid");
- const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
- try {
- const res = await fetch('/api/change-profile', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({ userid, ...tempUserInfo }),
- });
- if (res.ok) {
- alert("信息已保存!");
- } else {
- alert("保存失败,请重试。");
- }
- } catch (err) {
- alert("保存失败,请检查网络连接。");
- console.error("保存用户信息失败", err);
- }
+ alert("信息已保存!");
};
const handleAvatarClick = () => {
@@ -127,7 +39,7 @@
<AccountCircleIcon style={{ fontSize: 90, color: '#1a237e', marginBottom: 12 }} />
{tempUserInfo.avatar && (
<img
- src={tempUserInfo.avatar_url}
+ src={tempUserInfo.avatar}
alt="用户头像"
style={{
position: 'absolute',
@@ -155,19 +67,21 @@
</div>
<div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
<b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>邮箱:</b>
- <span
- style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}
- >
- {tempUserInfo.email}
- </span>
+ <input
+ type="email"
+ value={tempUserInfo.email}
+ onChange={(e) => handleInputChange("email", e.target.value)}
+ style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}
+ />
</div>
<div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
- <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>邀请剩余:</b>
- <span
- style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}
- >
- {tempUserInfo.invite_left}
- </span>
+ <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>公司:</b>
+ <input
+ type="text"
+ value={tempUserInfo.company}
+ onChange={(e) => handleInputChange("company", e.target.value)}
+ style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}
+ />
</div>
<div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
<b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>学校:</b>
@@ -179,21 +93,22 @@
/>
</div>
<div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
- <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>账号状态:</b>
- <span
- style={{ flex: 1, display: 'flex', alignItems: 'center', padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}
- >
- {tempUserInfo.account_status === 1 || tempUserInfo.account_status === "1" ? "封禁" : "正常"}
- <span style={{
- display: 'inline-block',
- width: 12,
- height: 12,
- borderRadius: '50%',
- backgroundColor: tempUserInfo.account_status === 1 || tempUserInfo.account_status === "1" ? '#e53935' : '#43a047',
- marginLeft: 10,
- border: '1px solid #b2b2b2',
- }} />
- </span>
+ <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>生日:</b>
+ <input
+ type="date"
+ value={tempUserInfo.birthday}
+ onChange={(e) => handleInputChange("birthday", e.target.value)}
+ style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}
+ />
+ </div>
+ <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
+ <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>密码:</b>
+ <input
+ type="password"
+ value={tempUserInfo.password || ""}
+ onChange={(e) => handleInputChange("password", e.target.value)}
+ style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}
+ />
</div>
<div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
<b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>性别:</b>
@@ -272,61 +187,15 @@
{/* 上传种子列表 */}
<div style={{ gridColumn: '2 / 3', gridRow: '1 / 2', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', padding: '20px' }}>
<h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18 }}>个人上传种子列表</h3>
- <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, minHeight: 60, padding: 12 }}>
- {userSeeds.length === 0 ? (
- <div style={{ color: '#b2b2b2', fontSize: 18, textAlign: 'center' }}>(暂无上传种子)</div>
- ) : (
- <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
- {userSeeds.map((seed, idx) => (
- <li
- key={seed.seed_id || idx}
- style={{ display: 'flex', alignItems: 'center', padding: '10px 0', borderBottom: '1px solid #e0e7ff', cursor: 'pointer' }}
- onClick={e => {
- // 阻止点击删除按钮时跳转
- if (e.target.classList.contains('delete-btn')) return;
- navigate(`/torrent/${seed.seed_id}`);
- }}
- >
- <span style={{ flex: 2, fontWeight: 500, color: '#1a237e', textDecoration: 'underline' }}>{seed.title}</span>
- <span style={{ flex: 1, color: '#5c6bc0' }}>{seed.tags}</span>
- <span style={{ flex: 1, color: '#ff9800', textAlign: 'right' }}>人气: {seed.popularity}</span>
- <button
- className="delete-btn"
- style={{ marginLeft: 18, background: '#e53935', color: '#fff', border: 'none', borderRadius: 6, padding: '4px 14px', cursor: 'pointer', fontSize: 14 }}
- onClick={async (e) => {
- e.stopPropagation();
- // const userid = localStorage.getItem("userid");
- const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
- try {
- const res = await fetch('/api/delete-seed', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ seed_id: seed.seed_id, userid }),
- });
- if (res.ok) {
- setUserSeeds(userSeeds.filter((s, i) => (s.seed_id || i) !== (seed.seed_id || idx)));
- } else {
- alert('删除失败,请重试');
- }
- } catch (err) {
- alert('删除失败,请检查网络');
- }
- }}
- >删除</button>
- </li>
- ))}
- </ul>
- )}
+ <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#b2b2b2', fontSize: 18 }}>
+ (此处显示上传种子列表)
</div>
</div>
{/* 活跃度模块 */}
<div style={{ gridColumn: '2 / 3', gridRow: '2 / 3', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', padding: '20px' }}>
<h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18 }}>活跃度</h3>
- <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, minHeight: 60, padding: 18, display: 'flex', flexDirection: 'column', gap: 12, fontSize: 18 }}>
- <div>魔力值:<b style={{ color: '#1976d2' }}>{userStats.magic}</b></div>
- <div>上传量:<b style={{ color: '#43a047' }}>{userStats.upload} GB</b></div>
- <div>下载量:<b style={{ color: '#e53935' }}>{userStats.download} GB</b></div>
- <div>上传/下载值:<b style={{ color: '#ff9800' }}>{userStats.ratio}</b></div>
+ <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#b2b2b2', fontSize: 18 }}>
+ (此处显示活跃度信息)
</div>
</div>
</div>